C.S.Burner 🪙 ━►🔥GIT Node
Node / reticulum_mirror / MeshChatX / files / meshchatx / src / frontend / components / interfaces / AddInterfacePage.vue
Displaying Raw • Download
meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue 67e78aa299d1bde9de61fcc4dc3e95a36eeb92d6 (67e78aa2) Text, 225.18 KB
T8b949e<!-- SPDX-License-Identifier: 0BSD AND MIT -->
Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col flex-1 overflow-hidden min-w-0 bg-slate-50 dark:bg-zinc-950Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffoverflow-y-auto flex-1 min-h-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffw-full max-w-[1920px] mx-auto px-4 md:px-5 lg:px-8 py-4 md:py-6 lg:py-8 space-y-6Ta5d6ff"Tff7b72>
T8b949e<!-- Header Section -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col sm:flex-row sm:items-center justify-between gap-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<h1 Te6edf3class=Ta5d6ff"Ta5d6fftext-2xl font-bold text-gray-900 dark:text-white flex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3:icon-name=Ta5d6ff"Ta5d6ffisEditingInterface ? Ta5d6ff'Ta5d6ffpencilTa5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffplus-circle-outlineTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-7 text-blue-500Ta5d6ff"
Tff7b72/>
{{ isEditingInterface ? Te6edf3$t("interfaces.edit_interface") : Te6edf3$t("interfaces.add_interface") }}
Tff7b72</h1>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-zinc-400 mt-1Ta5d6ff"Tff7b72>
{{
isEditingInterface
? "Update existing connection settings."
: "Create a new connection to the Reticulum network."
}}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex gap-2 shrink-0Ta5d6ff"Tff7b72>
Tff7b72<RouterLink Te6edf3:to=Ta5d6ff"Ta5d6ff{ name: Ta5d6ff'Ta5d6ffinterfacesTa5d6ff'Ta5d6ff }Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chipTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffarrow-leftTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Back to List
Tff7b72</RouterLink>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col-reverse gap-8 xl:flex-row xl:items-start xl:gap-10Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 min-w-0 space-y-6 xl:max-w-4xl 2xl:max-w-5xlTa5d6ff"Tff7b72>
T8b949e<!-- Main Form Card -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffglass-card space-y-8Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 lg:grid-cols-2 gap-8Ta5d6ff"Tff7b72>
T8b949e<!-- Basic Info Column -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-6Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 pb-2 border-b border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffinformation-outlineTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-5 text-gray-400Ta5d6ff"
Tff7b72/>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>Basic ConfigurationTff7b72</h3>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Interface NameTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceNameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffisEditingInterfaceTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. Home Node or Mobile TCPTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[isEditingInterface ? Ta5d6ff'Ta5d6ffcursor-not-allowed opacity-60Ta5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ff'Ta5d6ff]Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Transport TypeTff7b72</FormLabel>
T8b949e<!-- Visual Transport Selection -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-2Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6fftype in [
{
id: Ta5d6ff'Ta5d6ffTCPClientInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffTCP ClientTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6fflan-connectTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-blue-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffBackboneInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffBackboneTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6fftransit-connection-variantTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-sky-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffTCPServerInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffTCP ServerTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffserver-networkTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-indigo-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffUDPInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffUDPTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffbroadcastTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-cyan-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffRNode (LoRa)Ta5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffradio-handheldTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-emerald-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffI2PInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffI2P TunnelTa5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6fftunnelTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-purple-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffSerialInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffSerial (Generic)Ta5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffserial-portTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-amber-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffKISSInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffKISS (TNC)Ta5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffradio-towerTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-orange-500Ta5d6ff'Ta5d6ff,
},
{
id: Ta5d6ff'Ta5d6ffAutoInterfaceTa5d6ff'Ta5d6ff,
name: Ta5d6ff'Ta5d6ffAuto (Local)Ta5d6ff'Ta5d6ff,
icon: Ta5d6ff'Ta5d6ffauto-fixTa5d6ff'Ta5d6ff,
color: Ta5d6ff'Ta5d6fftext-pink-500Ta5d6ff'Ta5d6ff,
},
]Ta5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6fftype.idTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col items-center justify-center p-3 rounded-2xl border transition-all duration-200 text-center gap-1 groupTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[
newInterfaceType === type.id
? Ta5d6ff'Ta5d6ffbg-blue-500/10 border-blue-500 ring-1 ring-blue-500/50Ta5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6ffbg-gray-50/50 dark:bg-zinc-800/30 border-gray-100 dark:border-zinc-800 hover:border-gray-300 dark:hover:border-zinc-600Ta5d6ff'Ta5d6ff,
]Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffnewInterfaceType = type.idTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3:icon-name=Ta5d6ff"Ta5d6fftype.iconTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-6 transition-transform group-hover:scale-110Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[
newInterfaceType === type.id ? Ta5d6ff'Ta5d6fftext-blue-500Ta5d6ff'Ta5d6ff : type.color,
]Ta5d6ff"
Tff7b72/>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-bold uppercase tracking-tightTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[
newInterfaceType === type.id
? Ta5d6ff'Ta5d6fftext-blue-700 dark:text-blue-400Ta5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-600 dark:text-zinc-400Ta5d6ff'Ta5d6ff,
]Ta5d6ff"
Tff7b72>
{{ type.name }}
Tff7b72</span>
Tff7b72</button>
Tff7b72</div>
T8b949e<!-- Fallback/More select for less common types -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmt-3Ta5d6ff"Tff7b72>
Tff7b72<select
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTypeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field appearance-none pr-10 py-1.5! text-[11px]! opacity-70 hover:opacity-100Ta5d6ff"
Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>More options...Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffAX25KISSInterfaceTa5d6ff"Tff7b72>AX.25 KISS (Amateur Radio)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffLocalInterfaceTa5d6ff"Tff7b72>Local Interface (Loopback)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffPipeInterfaceTa5d6ff"Tff7b72>Pipe Interface (External)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffRNodeIPInterfaceTa5d6ff"Tff7b72>RNode over IPTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffBackboneInterfaceTa5d6ff"Tff7b72>Backbone (public relay)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ff__external__Ta5d6ff"Tff7b72>
Custom / external module (RNS interfacepath)
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- Dynamic Interface Specific Settings Column -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-6Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 pb-2 border-b border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcog-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5 text-gray-400Ta5d6ff" Tff7b72/>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>Connection DetailsTff7b72</h3>
Tff7b72</div>
T8b949e<!-- No selection placeholder -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff!newInterfaceTypeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffh-48 flex flex-col items-center justify-center text-center p-6 border-2 border-dashed border-gray-100 dark:border-zinc-800 rounded-3xlTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffarrow-left-boldTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-10 text-gray-200 dark:text-zinc-800 animate-bounce-leftTa5d6ff"
Tff7b72/>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-400 dark:text-zinc-600 mt-2Ta5d6ff"Tff7b72>
Select an interface type to configure connection settings.
Tff7b72</p>
Tff7b72</div>
T8b949e<!-- Interface Specific Fields -->
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-6 animate-in fade-in slide-in-from-right-4 duration-300Ta5d6ff"Tff7b72>
T8b949e<!-- TCP Client -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffTCPClientInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Target HostTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTargetHostTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. 1.2.3.4 or example.comTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Target PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTargetPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6fftcp-kiss-framingTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceKISSFramingEnabledTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6fftcp-kiss-framingTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>Use KISS framing (legacy compatibility)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6fftcp-i2p-tunneledTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceI2PTunnelingEnabledTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6fftcp-i2p-tunneledTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>I2P Tunneled (target is an I2P b32)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6fftcp-bootstrap-onlyTa5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBootstrapOnlyTa5d6ff" Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6fftcp-bootstrap-onlyTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-sm blockTa5d6ff"
Tff7b72>{{
Te6edf3$t("interfaces.discovery_default_bootstrap_only")
}}Tff7b72</FormLabel
>
Tff7b72<BundledDocsHint
Te6edf3paragraph-class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-gray-400 mt-0.5Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-3 gap-3Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Connect Timeout (s)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceConnectTimeoutTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffdefaultTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Max Reconnect TriesTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceMaxReconnectTriesTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffdefaultTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Fixed MTUTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceFixedMTUTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3:min=Ta5d6ff"Ta5d6ffreticulumMinFixedMtuTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffautoTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6ffmt-1 text-xs text-gray-500 dark:text-zinc-400Ta5d6ff"Tff7b72>
{{
Te6edf3$t("interfaces.fixed_mtu_hint", {
min: reticulumMinFixedMtu,
})
}}
Tff7b72</p>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffBackboneInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6ffbackbone-listen-modeTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenModeTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6ffbackbone-listen-modeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>Listener mode (host this backbone)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ff!newInterfaceBackboneListenModeTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Remote hostTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTargetHostTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. example.com or 1.2.3.4Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Target portTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTargetPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.backbone_transport_identity_label")
}}Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTransportIdentityTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"
Ta5d6ff Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.backbone_transport_identity_placeholderTa5d6ff'Ta5d6ff)
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field font-mono text-xsTa5d6ff"
Tff7b72/>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-gray-400 mt-1Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.backbone_transport_identity_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6ffbackbone-bootstrap-onlyTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBootstrapOnlyTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6ffbackbone-bootstrap-onlyTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-sm blockTa5d6ff"
Tff7b72>{{
Te6edf3$t("interfaces.discovery_default_bootstrap_only")
}}Tff7b72</FormLabel
>
Tff7b72<BundledDocsHint
Te6edf3paragraph-class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-gray-400 mt-0.5Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"
Tff7b72>Listen IP (optional if Device is set)Tff7b72</FormLabel
>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenIpTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff0.0.0.0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Listen PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Device (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenDeviceTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffKernel interface e.g. eth0, wlan0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"
Ta5d6ff hostKernelInterfacesLoading ||
hostKernelInterfaces.length ||
hostKernelInterfacesUnavailable
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-100 dark:border-zinc-800 bg-gray-50/50 dark:bg-zinc-900/30 p-3 space-y-2Ta5d6ff"
Tff7b72>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-600 dark:text-zinc-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.kernel_iface_picker_title") }}
Tff7b72</p>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffhostKernelInterfacesLoadingTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-400Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.kernel_iface_loading") }}
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-1.5 max-h-36 overflow-y-autoTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6ffiface in hostKernelInterfacesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ff'Ta5d6ffbb-Ta5d6ff'Ta5d6ff + iface.nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffpx-2.5 py-1.5 text-left text-xs rounded-lg border border-gray-200 dark:border-zinc-700 bg-white/90 dark:bg-zinc-900/90 hover:border-blue-400 dark:hover:border-blue-500 transition-colors max-w-fullTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffnewInterfaceBackboneListenDevice = iface.nameTa5d6ff"
Tff7b72>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6fffont-mono font-medium text-gray-900 dark:text-zinc-100Ta5d6ff"
Tff7b72>{{ iface.name }}Tff7b72</span
>
Tff7b72<span
Te6edf3v-if=Ta5d6ff"Ta5d6ffiface.addresses && iface.addresses.lengthTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffblock text-[10px] text-gray-500 dark:text-zinc-500 truncate max-w-[20rem]Ta5d6ff"
Tff7b72>{{
iface.addresses.slice(0, 3).join(" \u00b7 ")
}}Tff7b72</span
>
Tff7b72</button>
Tff7b72</div>
Tff7b72<p
Te6edf3v-if=Ta5d6ff"Ta5d6ffhostKernelInterfacesUnavailableTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-amber-600 dark:text-amber-500Ta5d6ff"
Tff7b72>
{{ hostKernelInterfacesUnavailable }}
Tff7b72</p>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-500Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.kernel_iface_picker_help") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6ffbackbone-listen-ipv6Ta5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePreferIPV6Ta5d6ff"
Tff7b72/>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6ffbackbone-listen-ipv6Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>Prefer IPv6Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-100 dark:border-zinc-800 bg-gray-50/50 dark:bg-zinc-900/30 p-3 space-y-3Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start justify-between gap-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label mb-0!Ta5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.block_fast_flapping_label")
}}Tff7b72</FormLabel>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-gray-400 mt-1Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.block_fast_flapping_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBlockFastFlappingTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 md:grid-cols-3 gap-3Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.fast_flapping_block_time_label")
}}Tff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffnewInterfaceFastFlappingBlockTimeTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.fast_flapping_threshold_label")
}}Tff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffnewInterfaceFastFlappingThresholdTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0.1Ta5d6ff"
Te6edf3step=Ta5d6ff"Ta5d6ff0.1Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.fast_flapping_grace_label")
}}Tff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffnewInterfaceFastFlappingGraceTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- TCP Server / UDP -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffTCPServerInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffUDPInterfaceTa5d6ff'Ta5d6ff].includes(newInterfaceType)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"
Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Listen IPTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceListenIpTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff0.0.0.0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
required
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Listen PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceListenPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Device (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceNetworkDeviceTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. eth0 (real OS interface; leave empty to use Listen IP only)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"
Ta5d6ff hostKernelInterfacesLoading ||
hostKernelInterfaces.length ||
hostKernelInterfacesUnavailable
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-100 dark:border-zinc-800 bg-gray-50/50 dark:bg-zinc-900/30 p-3 space-y-2Ta5d6ff"
Tff7b72>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-600 dark:text-zinc-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.kernel_iface_picker_title") }}
Tff7b72</p>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffhostKernelInterfacesLoadingTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.kernel_iface_loading") }}
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-1.5 max-h-36 overflow-y-autoTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6ffiface in hostKernelInterfacesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ff'Ta5d6ffsrv-Ta5d6ff'Ta5d6ff + iface.nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffpx-2.5 py-1.5 text-left text-xs rounded-lg border border-gray-200 dark:border-zinc-700 bg-white/90 dark:bg-zinc-900/90 hover:border-blue-400 dark:hover:border-blue-500 transition-colors max-w-fullTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffnewInterfaceNetworkDevice = iface.nameTa5d6ff"
Tff7b72>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6fffont-mono font-medium text-gray-900 dark:text-zinc-100Ta5d6ff"
Tff7b72>{{ iface.name }}Tff7b72</span
>
Tff7b72<span
Te6edf3v-if=Ta5d6ff"Ta5d6ffiface.addresses && iface.addresses.lengthTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffblock text-[10px] text-gray-500 dark:text-zinc-500 truncate max-w-[20rem]Ta5d6ff"
Tff7b72>{{ iface.addresses.slice(0, 3).join(" \u00b7 ") }}Tff7b72</span
>
Tff7b72</button>
Tff7b72</div>
Tff7b72<p
Te6edf3v-if=Ta5d6ff"Ta5d6ffhostKernelInterfacesUnavailableTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-amber-600 dark:text-amber-500Ta5d6ff"
Tff7b72>
{{ hostKernelInterfacesUnavailable }}
Tff7b72</p>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-500Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.kernel_iface_picker_help") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffTCPServerInterfaceTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap items-center gap-4Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6fftcp-server-ipv6Ta5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePreferIPV6Ta5d6ff" Tff7b72/>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6fftcp-server-ipv6Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>Prefer IPv6Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6fftcp-server-i2pTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceI2PTunnelingEnabledTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6fftcp-server-i2pTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>I2P TunneledTff7b72</FormLabel
>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- UDP Extras -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffUDPInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Forward IPTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceForwardIpTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff255.255.255.255Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Forward PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceForwardPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff4242Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- I2P Interface -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffI2PInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-amber-50/80 dark:bg-amber-900/20 p-3 rounded-2xl border border-amber-200 dark:border-amber-800/40 text-xs text-amber-900 dark:text-amber-200 space-y-1Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-semiboldTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.i2p_requirements_title") }}
Tff7b72</div>
Tff7b72<pTff7b72>{{ Te6edf3$t("interfaces.i2p_requirements_body") }}Tff7b72</p>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-50/50 dark:bg-blue-900/10 p-3 rounded-2xl border border-blue-100 dark:border-blue-900/20 text-xs text-blue-800 dark:text-blue-300Ta5d6ff"
Tff7b72>
To use the I2P interface, you must have an I2P router running on your
system with SAM enabled.
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff!transportEnabledTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffbg-red-50/80 dark:bg-red-900/20 p-3 rounded-2xl border border-red-200 dark:border-red-800/40 text-xs text-red-800 dark:text-red-200Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.i2p_transport_required") }}
Tff7b72</div>
Tff7b72<div
Te6edf3v-else-if=Ta5d6ff"Ta5d6ffhasExistingI2PInterface && !isEditingInterfaceTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffbg-red-50/80 dark:bg-red-900/20 p-3 rounded-2xl border border-red-200 dark:border-red-800/40 text-xs text-red-800 dark:text-red-200Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.i2p_already_exists") }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6ffi2p-connectableTa5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceConnectableTa5d6ff" Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6ffi2p-connectableTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>Allow incoming peers (connectable)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Initial Peers (Optional)Tff7b72</FormLabel>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6ff(peer, index) in I2PSettings.newInterfacePeersTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffindexTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"
Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffI2PSettings.newInterfacePeers[index]Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffb32.i2p addressTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-red-500 hover:text-red-400 p-1Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffremoveI2PPeer(index)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6fftrash-can-outlineTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff"
Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip py-1! px-3! text-[10px]!Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffaddI2PPeer(Ta5d6ff'Ta5d6ff'Ta5d6ff)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffplusTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-3Ta5d6ff" Tff7b72/> Add Peer
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- RNode / Hardware -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"
Ta5d6ff [
Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff,
Ta5d6ff'Ta5d6ffRNodeIPInterfaceTa5d6ff'Ta5d6ff,
Ta5d6ff'Ta5d6ffSerialInterfaceTa5d6ff'Ta5d6ff,
Ta5d6ff'Ta5d6ffKISSInterfaceTa5d6ff'Ta5d6ff,
Ta5d6ff'Ta5d6ffAX25KISSInterfaceTa5d6ff'Ta5d6ff,
].includes(newInterfaceType)
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col gap-3 pb-2 sm:flex-row sm:items-center sm:gap-6Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6ffrnode-use-ipTa5d6ff"
Te6edf3:model-value=Ta5d6ff"Ta5d6ffnewInterfaceRNodeUseIPTa5d6ff"
@Te6edf3update:model-value=Ta5d6ff"Ta5d6ffsetRNodeTransportIpTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6ffrnode-use-ipTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>Connect over network (IP)Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle
Te6edf3id=Ta5d6ff"Ta5d6ffrnode-use-bleTa5d6ff"
Te6edf3:model-value=Ta5d6ff"Ta5d6ffnewInterfaceRNodeUseBleTa5d6ff"
@Te6edf3update:model-value=Ta5d6ff"Ta5d6ffsetRNodeTransportBleTa5d6ff"
Tff7b72/>
Tff7b72<FormLabel
Te6edf3for=Ta5d6ff"Ta5d6ffrnode-use-bleTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>{{ Te6edf3$t("interfaces.rnode_ble_toggle") }}Tff7b72</FormLabel
>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceRNodeUseIP || newInterfaceType === Ta5d6ff'Ta5d6ffRNodeIPInterfaceTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2Ta5d6ff"
Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.rnode_ip_host_label")
}}Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceRNodeIPHostTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff10.0.0.1Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-500 leading-relaxedTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.rnode_tcp_port_fixed_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div
Te6edf3v-else-if=Ta5d6ff"
Ta5d6ff newInterfaceType === Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff && newInterfaceRNodeUseBle
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2Ta5d6ff"
Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.rnode_ble_peer_label")
}}Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceRNodeBlePeerTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.rnode_ble_peer_placeholderTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field font-mono text-smTa5d6ff"
Te6edf3autocapitalize=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3spellcheck=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Tff7b72/>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-[11px] leading-relaxed text-gray-500 dark:text-gray-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.rnode_ble_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div v-elseTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Serial PortTff7b72</FormLabel>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffrelativeTa5d6ff"Tff7b72>
Tff7b72<select
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePortTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field appearance-none pr-10Ta5d6ff"
Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff" disabledTff7b72>Select a port...Tff7b72</option>
Tff7b72<option
Te6edf3v-for=Ta5d6ff"Ta5d6ffport in comportsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffport.deviceTa5d6ff"
Te6edf3:value=Ta5d6ff"Ta5d6ffport.deviceTa5d6ff"
Tff7b72>
{{ port.device }} ({{ port.product ?? "?" }})
Tff7b72</option>
Tff7b72</select>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-y-0 right-0 flex items-center px-3 pointer-events-none text-gray-400Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffchevron-downTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmt-2 flex justify-endTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] uppercase font-bold text-blue-500 hover:text-blue-600 tracking-widerTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffloadComportsTa5d6ff"
Tff7b72>
Refresh Ports
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- RNode Radio Parameters -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffRNodeIPInterfaceTa5d6ff'Ta5d6ff].includes(newInterfaceType)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Frequency (Hz)Tff7b72</FormLabel>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1Ta5d6ff"Tff7b72>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffRNodeGHzValueTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field text-centerTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-center text-gray-400 mt-1Ta5d6ff"Tff7b72>
GHz
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1Ta5d6ff"Tff7b72>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffRNodeMHzValueTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field text-centerTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-center text-gray-400 mt-1Ta5d6ff"Tff7b72>
MHz
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1Ta5d6ff"Tff7b72>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffRNodekHzValueTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field text-centerTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-center text-gray-400 mt-1Ta5d6ff"Tff7b72>
kHz
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffformattedFrequencyTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffmt-2 text-center text-sm font-mono text-blue-500 font-boldTa5d6ff"
Tff7b72>
{{ formattedFrequency }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>BandwidthTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceBandwidthTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option
Te6edf3v-for=Ta5d6ff"Ta5d6ffbw in RNodeInterfaceDefaults.bandwidthsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffbwTa5d6ff"
Te6edf3:value=Ta5d6ff"Ta5d6ffbwTa5d6ff"
Tff7b72>
{{ bw / 1000 }} kHz
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Power (dBm)Tff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffnewInterfaceTxpowerTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3:min=Ta5d6ff"Ta5d6ffRNodeInterfaceDefaults.txpowerMinTa5d6ff"
Te6edf3:max=Ta5d6ff"Ta5d6ffRNodeInterfaceDefaults.txpowerMaxTa5d6ff"
Te6edf3step=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Spreading FactorTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceSpreadingFactorTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option
Te6edf3v-for=Ta5d6ff"Ta5d6ffsf in RNodeInterfaceDefaults.spreadingfactorsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffsfTa5d6ff"
Te6edf3:value=Ta5d6ff"Ta5d6ffsfTa5d6ff"
Tff7b72>
SF{{ sf }}
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Coding RateTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceCodingRateTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option
Te6edf3v-for=Ta5d6ff"Ta5d6ffcr in RNodeInterfaceDefaults.codingratesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffcrTa5d6ff"
Te6edf3:value=Ta5d6ff"Ta5d6ffcrTa5d6ff"
Tff7b72>
4:{{ cr }}
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6ffrnode-flow-controlTa5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceFlowControlTa5d6ff" Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6ffrnode-flow-controlTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>Hardware flow controlTff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Station ID CallsignTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIDCallsignTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. NOCALLTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>ID Interval (s)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIDIntervalTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff600Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Airtime Limit Long (%)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceAirtimeLimitLongTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff100Ta5d6ff"
Te6edf3step=Ta5d6ff"Ta5d6ff0.1Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptionalTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Airtime Limit Short (%)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceAirtimeLimitShortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff100Ta5d6ff"
Te6edf3step=Ta5d6ff"Ta5d6ff0.1Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptionalTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- Serial port settings (Serial/KISS/AX25KISS) -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"
Ta5d6ff [Ta5d6ff'Ta5d6ffSerialInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffKISSInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffAX25KISSInterfaceTa5d6ff'Ta5d6ff].includes(
newInterfaceType
)
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Baud RateTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceSpeedTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff9600Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Data BitsTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDatabitsTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (8)Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff5Ta5d6ff"Tff7b72>5Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff6Ta5d6ff"Tff7b72>6Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff7Ta5d6ff"Tff7b72>7Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff8Ta5d6ff"Tff7b72>8Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>ParityTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceParityTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (none)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffNTa5d6ff"Tff7b72>NoneTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffETa5d6ff"Tff7b72>EvenTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffOTa5d6ff"Tff7b72>OddTff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Stop BitsTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceStopbitsTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (1)Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff1Ta5d6ff"Tff7b72>1Tff7b72</option>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ff2Ta5d6ff"Tff7b72>2Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- KISS framing parameters (KISS/AX25KISS) -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffKISSInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffAX25KISSInterfaceTa5d6ff'Ta5d6ff].includes(newInterfaceType)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Preamble (ms)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePreambleTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff350Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>TX Tail (ms)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceTXTailTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff20Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Persistence (0-255)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfacePersistenceTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff255Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff64Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Slot Time (ms)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceSlotTimeTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff20Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<Toggle Te6edf3id=Ta5d6ff"Ta5d6ffkiss-flow-controlTa5d6ff" Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceFlowControlTa5d6ff" Tff7b72/>
Tff7b72<FormLabel Te6edf3for=Ta5d6ff"Ta5d6ffkiss-flow-controlTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffcursor-pointer mb-0! text-smTa5d6ff"
Tff7b72>Hardware flow controlTff7b72</FormLabel
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Beacon CallsignTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIDCallsignTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptionalTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Beacon Interval (s)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIDIntervalTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptionalTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- AX.25 KISS extras -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffAX25KISSInterfaceTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>AX.25 CallsignTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceCallsignTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. NOCALLTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>SSID (0-15)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceSSIDTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff15Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- AutoInterface -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffAutoInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-pink-50/50 dark:bg-pink-900/10 p-3 rounded-2xl border border-pink-100 dark:border-pink-900/20 text-xs text-pink-800 dark:text-pink-300Ta5d6ff"
Tff7b72>
ⓘ Auto Interface auto-discovers peers on connected networks via IPv6
multicast.
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Group ID (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceGroupIDTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffreticulumTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400 mt-1Ta5d6ff"Tff7b72>
Only peers sharing the same group_id will discover each other.
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Discovery ScopeTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDiscoveryScopeTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (link)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6fflinkTa5d6ff"Tff7b72>Link (local segment)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffadminTa5d6ff"Tff7b72>AdministrativeTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffsiteTa5d6ff"Tff7b72>SiteTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6fforganisationTa5d6ff"Tff7b72>OrganisationTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffglobalTa5d6ff"Tff7b72>GlobalTff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Multicast Address TypeTff7b72</FormLabel>
Tff7b72<select
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceMulticastAddressTypeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffnullTa5d6ff"Tff7b72>Default (temporary)Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6fftemporaryTa5d6ff"Tff7b72>TemporaryTff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffpermanentTa5d6ff"Tff7b72>PermanentTff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Discovery PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDiscoveryPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff65535Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff29716Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Data PortTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDataPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3max=Ta5d6ff"Ta5d6ff65535Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff42671Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"
Tff7b72>Allowed Devices (comma separated)Tff7b72</FormLabel
>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceDevicesTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. eth0, wlan0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"
Ta5d6ff hostKernelInterfacesLoading ||
hostKernelInterfaces.length ||
hostKernelInterfacesUnavailable
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffmt-1.5 space-y-1Ta5d6ff"
Tff7b72>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-500 dark:text-zinc-500Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.auto_iface_ifname_chips_hint") }}
Tff7b72</p>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffhostKernelInterfacesLoadingTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.kernel_iface_loading") }}
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-1 max-h-20 overflow-y-autoTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6ffiface in hostKernelInterfacesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ff'Ta5d6ffauto-allow-Ta5d6ff'Ta5d6ff + iface.nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-full truncate px-1.5 py-0.5 text-[10px] rounded border font-mono transition-colorsTa5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff autoInterfaceChipActive(
Ta5d6ff'Ta5d6ffnewInterfaceDevicesTa5d6ff'Ta5d6ff,
iface.name
)
? Ta5d6ff'Ta5d6ffborder-blue-400 bg-blue-50/90 text-blue-900 dark:border-blue-500 dark:bg-blue-950/40 dark:text-blue-200Ta5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6ffborder-gray-200 bg-white/80 text-gray-800 hover:border-blue-300 dark:border-zinc-700 dark:bg-zinc-900/80 dark:text-zinc-200 dark:hover:border-blue-500Ta5d6ff'
Ta5d6ff Ta5d6ff"
@Te6edf3click=Ta5d6ff"
Ta5d6ff toggleAutoInterfaceCommaToken(
Ta5d6ff'Ta5d6ffnewInterfaceDevicesTa5d6ff'Ta5d6ff,
iface.name
)
Ta5d6ff"
Tff7b72>
{{ iface.name }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"
Tff7b72>Ignored Devices (comma separated)Tff7b72</FormLabel
>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceIgnoredDevicesTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. tun0, docker0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"
Ta5d6ff hostKernelInterfacesLoading ||
hostKernelInterfaces.length ||
hostKernelInterfacesUnavailable
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffmt-1.5 space-y-1Ta5d6ff"
Tff7b72>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-500 dark:text-zinc-500Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.auto_iface_ifname_chips_hint") }}
Tff7b72</p>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffhostKernelInterfacesLoadingTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.kernel_iface_loading") }}
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-1 max-h-20 overflow-y-autoTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6ffiface in hostKernelInterfacesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ff'Ta5d6ffauto-ignore-Ta5d6ff'Ta5d6ff + iface.nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-full truncate px-1.5 py-0.5 text-[10px] rounded border font-mono transition-colorsTa5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff autoInterfaceChipActive(
Ta5d6ff'Ta5d6ffnewInterfaceIgnoredDevicesTa5d6ff'Ta5d6ff,
iface.name
)
? Ta5d6ff'Ta5d6ffborder-amber-400 bg-amber-50/90 text-amber-950 dark:border-amber-600 dark:bg-amber-950/40 dark:text-amber-100Ta5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6ffborder-gray-200 bg-white/80 text-gray-800 hover:border-amber-300 dark:border-zinc-700 dark:bg-zinc-900/80 dark:text-zinc-200 dark:hover:border-amber-600Ta5d6ff'
Ta5d6ff Ta5d6ff"
@Te6edf3click=Ta5d6ff"
Ta5d6ff toggleAutoInterfaceCommaToken(
Ta5d6ff'Ta5d6ffnewInterfaceIgnoredDevicesTa5d6ff'Ta5d6ff,
iface.name
)
Ta5d6ff"
Tff7b72>
{{ iface.name }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Configured Bitrate (bps)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceConfiguredBitrateTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffoptional overrideTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- Pipe Interface -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffPipeInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-gray-50/50 dark:bg-zinc-800/30 p-3 rounded-2xl border border-gray-100 dark:border-zinc-800 text-xs text-gray-600 dark:text-zinc-400Ta5d6ff"
Tff7b72>
ⓘ Interface with external programs via stdin/stdout.
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>CommandTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceCommandTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffe.g. netcat -l 5757Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Respawn Delay (s)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewInterfaceRespawnDelayTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ff5Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- LocalInterface: IPC path used internally by RNS; optional external module -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ffLocalInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-800 dark:text-zinc-200 leading-snugTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.loopback_local_title") }}
Tff7b72</div>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-600 dark:text-zinc-400 leading-relaxedTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.loopback_local_body") }}
Tff7b72</p>
Tff7b72<BundledDocsHint
Te6edf3hint-i18n-key=Ta5d6ff"Ta5d6ffinterfaces.loopback_local_docs_hintTa5d6ff"
Te6edf3link-i18n-key=Ta5d6ff"Ta5d6ffinterfaces.loopback_local_docs_linkTa5d6ff"
Te6edf3:docs-rel-path=Ta5d6ff"Ta5d6ffdocsReticulumInterfacesOverviewTa5d6ff"
Te6edf3paragraph-class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-gray-400Ta5d6ff"
Tff7b72/>
Tff7b72</div>
T8b949e<!-- External interface module (TypeName.py under Reticulum interfacepath) -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffnewInterfaceType === Ta5d6ff'Ta5d6ff__external__Ta5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-600 dark:text-zinc-400 leading-relaxedTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.custom_external_intro") }}
Tff7b72</p>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-amber-200/80 dark:border-amber-900/40 bg-amber-50/40 dark:bg-amber-950/20 p-3 space-y-2Ta5d6ff"
Tff7b72>
Tff7b72<p
Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-amber-900 dark:text-amber-200/90 leading-relaxedTa5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.custom_external_install_intro") }}
Tff7b72</p>
Tff7b72<p
Te6edf3v-if=Ta5d6ff"Ta5d6ffinterfaceModulesPathTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-mono text-amber-800/80 dark:text-amber-200/70 break-allTa5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.custom_external_interfacepath_label") }}:
{{ interfaceModulesPath }}
Tff7b72</p>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap items-center gap-2Ta5d6ff"Tff7b72>
Tff7b72<input
Te6edf3ref=Ta5d6ff"Ta5d6ffinterface-module-file-inputTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fffileTa5d6ff"
Te6edf3accept=Ta5d6ff"Ta5d6ff.py,text/x-python,text/plainTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffhiddenTa5d6ff"
@Te6edf3change=Ta5d6ff"Ta5d6ffonInterfaceModuleFileSelectedTa5d6ff"
Tff7b72/>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip py-1.5! px-3! text-[10px]!Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffinterfaceModuleBusyTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffpickInterfaceModuleFileTa5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.custom_external_install_button") }}
Tff7b72</button>
Tff7b72<label
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-1.5 text-[10px] text-gray-600 dark:text-zinc-400Ta5d6ff"
Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffinterfaceModuleOverwriteTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffcheckboxTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-smTa5d6ff"
Tff7b72/>
{{ Te6edf3$t("interfaces.custom_external_install_overwrite") }}
Tff7b72</label>
Tff7b72</div>
Tff7b72<ul
Te6edf3v-if=Ta5d6ff"Ta5d6ffinstalledInterfaceModules.lengthTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-mono text-gray-600 dark:text-zinc-400 space-y-1Ta5d6ff"
Tff7b72>
Tff7b72<li
Te6edf3v-for=Ta5d6ff"Ta5d6ffmod in installedInterfaceModulesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffmod.filenameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-between gap-2Ta5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-left hover:underlineTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffcustomExternalTypeName = mod.typeTa5d6ff"
Tff7b72>
{{ mod.filename }}
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-red-500 hover:text-red-600 shrink-0Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffinterfaceModuleBusyTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.custom_external_module_deleteTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffdeleteInstalledInterfaceModule(mod.type)Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.custom_external_module_delete") }}
Tff7b72</button>
Tff7b72</li>
Tff7b72</ul>
Tff7b72<p v-else Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-500 dark:text-zinc-500Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.custom_external_modules_empty") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.custom_external_type_label")
}}Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffcustomExternalTypeNameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field font-mono text-xsTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.custom_external_type_placeholderTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3spellcheck=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.custom_external_json_label")
}}Tff7b72</FormLabel>
Tff7b72<textarea
Te6edf3v-model=Ta5d6ff"Ta5d6ffcustomExternalOptionsJsonTa5d6ff"
Te6edf3rows=Ta5d6ff"Ta5d6ff10Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field font-mono text-xs min-h-[10rem]Ta5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.custom_external_json_placeholderTa5d6ff'Ta5d6ff)Ta5d6ff"
Tff7b72>Tff7b72</textarea>
Tff7b72</div>
Tff7b72<BundledDocsHint
Te6edf3hint-i18n-key=Ta5d6ff"Ta5d6ffinterfaces.custom_external_docs_hintTa5d6ff"
Te6edf3link-i18n-key=Ta5d6ff"Ta5d6ffinterfaces.custom_external_docs_linkTa5d6ff"
Te6edf3:docs-rel-path=Ta5d6ff"Ta5d6ffdocsReticulumInterfacesOverviewTa5d6ff"
Te6edf3paragraph-class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-gray-400Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
T8b949e<!-- Advanced Sections -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4Ta5d6ff"Tff7b72>
T8b949e<!-- RNode Advanced Tools -->
Tff7b72<ExpandingSection
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffRNodeIPInterfaceTa5d6ff'Ta5d6ff].includes(newInterfaceType)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-0! overflow-hiddenTa5d6ff"
Tff7b72>
Tff7b72<template #title
Tff7b72>Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"Tff7b72>Calculated ParametersTff7b72</span>Tff7b72</template
>
Tff7b72<template #contentTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Antenna Gain (dBi)Tff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffRNodeInterfaceLoRaParameters.antennaGainTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-3 gap-3Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-500/5 p-3 rounded-2xl border border-blue-500/10 text-centerTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] uppercase font-bold text-blue-500 mb-1Ta5d6ff"Tff7b72>
Sensitivity
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-mono font-boldTa5d6ff"Tff7b72>
{{ RNodeInterfaceLoRaParameters.sensitivity ?? "???" }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-500/5 p-3 rounded-2xl border border-blue-500/10 text-centerTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] uppercase font-bold text-blue-500 mb-1Ta5d6ff"Tff7b72>
Data Rate
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-mono font-boldTa5d6ff"Tff7b72>
{{ RNodeInterfaceLoRaParameters.dataRate ?? "???" }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-500/5 p-3 rounded-2xl border border-blue-500/10 text-centerTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] uppercase font-bold text-blue-500 mb-1Ta5d6ff"Tff7b72>
Link Budget
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-mono font-boldTa5d6ff"Tff7b72>
{{ RNodeInterfaceLoRaParameters.linkBudget ?? "???" }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72</ExpandingSection>
T8b949e<!-- Interface Discovery Settings -->
Tff7b72<ExpandingSection Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-0! overflow-hiddenTa5d6ff"Tff7b72>
Tff7b72<template #title
Tff7b72>Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"Tff7b72>Interface DiscoveryTff7b72</span>Tff7b72</template
>
Tff7b72<template #contentTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-betweenTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label mb-0!Ta5d6ff"
Tff7b72>Publish Discovery AnnounceTff7b72</FormLabel
>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-400Ta5d6ff"Tff7b72>
Makes your node visible to others on the network.
Tff7b72</p>
Tff7b72</div>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.discoverableTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffdiscovery.discoverableTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800 animate-in fade-in slide-in-from-top-2Ta5d6ff"
Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Discovery NameTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.discovery_nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffHuman-friendly nameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Announce Interval (m)Tff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffdiscovery.announce_intervalTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Reachable OnTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.reachable_onTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffIP or HostnameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 md:grid-cols-3 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Latitude (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.latitudeTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3inputmode=Ta5d6ff"Ta5d6ffdecimalTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3aria-required=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffLeave blank if unknownTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Longitude (optional)Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.longitudeTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3inputmode=Ta5d6ff"Ta5d6ffdecimalTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3aria-required=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffLeave blank if unknownTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"
Tff7b72>Height in metres (optional)Tff7b72</FormLabel
>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.heightTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3inputmode=Ta5d6ff"Ta5d6ffdecimalTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3aria-required=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffLeave blank if unknownTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.location_cmd_label")
}}Tff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.location_cmdTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.location_cmd_placeholderTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field font-mono text-xsTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Tff7b72/>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-gray-400 mt-1Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.location_cmd_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Discovery stamp valueTff7b72</FormLabel>
Tff7b72<input
v-model.Te6edf3number=Ta5d6ff"Ta5d6ffdiscovery.discovery_stamp_valueTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3min=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap items-center justify-between gap-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-between gap-4 max-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label mb-0!Ta5d6ff"
Tff7b72>Encrypt discoveryTff7b72</FormLabel
>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.discovery_encryptTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-between gap-4 max-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label mb-0!Ta5d6ff"
Tff7b72>Publish IFAC in announceTff7b72</FormLabel
>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffdiscovery.publish_ifacTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72</ExpandingSection>
T8b949e<!-- Global Discovery Settings -->
Tff7b72<ExpandingSection Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-0! overflow-hiddenTa5d6ff"Tff7b72>
Tff7b72<template #title
Tff7b72>Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"Tff7b72>Discovery Listener (Peer)Tff7b72</span>Tff7b72</template
>
Tff7b72<template #contentTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-betweenTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label mb-0!Ta5d6ff"
Tff7b72>Enable Discovery ListenerTff7b72</FormLabel
>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-400Ta5d6ff"Tff7b72>
Listen for announced interfaces and optionally auto-connect.
Tff7b72</p>
Tff7b72</div>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffreticulumDiscovery.discover_interfacesTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between pt-2Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-xl min-w-0Ta5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label mb-0! text-smTa5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.discovery_default_bootstrap_only")
}}Tff7b72</FormLabel>
Tff7b72<BundledDocsHint Te6edf3paragraph-class=Ta5d6ff"Ta5d6fftext-xs text-gray-400Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffreticulumDiscovery.default_bootstrap_onlyTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffreticulumDiscovery.discover_interfacesTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800 animate-in fade-in slide-in-from-top-2Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 md:grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffreticulumDiscovery.interface_discovery_whitelistTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffWhitelist (names, hosts, IDs)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffreticulumDiscovery.interface_discovery_blacklistTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffBlacklist (names, hosts, IDs)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex justify-endTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip text-[10px]!Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffsavingDiscoveryTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffsaveReticulumDiscoveryConfigTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcontent-saveTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-3Ta5d6ff" Tff7b72/>
Save Listener Prefs
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72</ExpandingSection>
T8b949e<!-- Shared Advanced Settings -->
Tff7b72<ExpandingSection Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-0! overflow-hiddenTa5d6ff"Tff7b72>
Tff7b72<template #title
Tff7b72>Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"
Tff7b72>Advanced Parameters (IFAC, Mode)Tff7b72</span
>Tff7b72</template
>
Tff7b72<template #contentTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Interface ModeTff7b72</FormLabel>
Tff7b72<select Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.modeTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"Tff7b72>
Tff7b72<option Te6edf3:value=Ta5d6ff"Ta5d6ffundefinedTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.mode_default_full") }}
Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6fffullTa5d6ff"Tff7b72>{{ Te6edf3$t("interfaces.mode_full") }}Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffgatewayTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.mode_gateway") }}
Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffaccess_pointTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.mode_access_point") }}
Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffroamingTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.mode_roaming") }}
Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffboundaryTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.mode_boundary") }}
Tff7b72</option>
Tff7b72<option Te6edf3value=Ta5d6ff"Ta5d6ffinternalTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.mode_internal") }}
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Forced BitrateTff7b72</FormLabel>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.bitrateTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffnumberTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffbpsTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-2Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start justify-between gap-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0 max-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label mb-0!Ta5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.recursive_prs_label")
}}Tff7b72</FormLabel>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-400 mt-1Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.recursive_prs_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.recursive_prsTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start justify-between gap-4Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0 max-w-mdTa5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-label mb-0!Ta5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.announces_from_internal_label")
}}Tff7b72</FormLabel>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-400 mt-1Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.announces_from_internal_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<Toggle Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.announces_from_internalTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 pt-4 border-t border-gray-100 dark:border-zinc-800Ta5d6ff"Tff7b72>
Tff7b72<FormLabel Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>Interface Access Code (IFAC)Tff7b72</FormLabel>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 md:grid-cols-2 gap-4Ta5d6ff"Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.network_nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffNetwork NameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffsharedInterfaceSettings.passphraseTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffPassphraseTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72</ExpandingSection>
Tff7b72</div>
T8b949e<!-- Footer Save Action -->
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffpt-8 flex items-center justify-between gap-4 border-t border-gray-200 dark:border-zinc-800Ta5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip px-10! py-3! text-sm!Ta5d6ff"
@Te6edf3click=Ta5d6ff"Te6edf3$router.pushTa5d6ff({ name: Ta5d6ff'Ta5d6ffinterfacesTa5d6ff'Ta5d6ff })Ta5d6ff"
Tff7b72>
Cancel
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip px-4 py-2 text-smTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffisSavingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffsaveInterfaceTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3:icon-name=Ta5d6ff"Ta5d6ffisSaving ? Ta5d6ff'Ta5d6ffloadingTa5d6ff'Ta5d6ff : isEditingInterface ? Ta5d6ff'Ta5d6ffcontent-saveTa5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffplusTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff{ Ta5d6ff'Ta5d6ffanimate-spinTa5d6ff'Ta5d6ff: isSaving }Ta5d6ff"
Tff7b72/>
{{ isEditingInterface ? "Update Connection" : "Create Connection" }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<aside
Te6edf3class=Ta5d6ff"Ta5d6ffw-full shrink-0 space-y-4 xl:w-96 xl:sticky xl:top-4 xl:self-start xl:max-h-[min(calc(100dvh-6rem),920px)] xl:overflow-y-auto xl:border-l border-gray-200/80 dark:border-zinc-800 xl:pl-8Ta5d6ff"
Te6edf3:aria-label=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.add_interface_sidebar_a11yTa5d6ff'Ta5d6ff)Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff!isEditingInterface && communityPresetsEnabled && communityInterfaces.length > 0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-0! overflow-hiddenTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-gray-50/50 dark:bg-zinc-800/50 p-4 border-b border-gray-200 dark:border-zinc-800 flex items-center justify-between gap-2Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<h2 Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-white flex items-center gap-2 text-smTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fflightning-boltTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5 text-yellow-500Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("interfaces.community_quick_start") }}
Tff7b72</h2>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-400 mt-0.5Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.community_quick_start_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-0.5 shrink-0Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-gray-400 hover:text-gray-600 dark:hover:text-zinc-200 transition-colors p-1 rounded-fullTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffrefreshingCommunityPresetsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.community_presets_refreshTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3:aria-label=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.community_presets_refreshTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffrefreshCommunityPresetsTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffrefreshTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff{ Ta5d6ff'Ta5d6ffanimate-spinTa5d6ff'Ta5d6ff: refreshingCommunityPresets }Ta5d6ff"
Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-gray-400 hover:text-gray-600 dark:hover:text-zinc-200 transition-colors p-1 shrink-0Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.community_quick_start_hideTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffupdateConfig({ show_suggested_community_interfaces: false })Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffdivide-y divide-gray-100 dark:divide-zinc-800 max-h-[min(50vh,28rem)] overflow-y-autoTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6ffcommunityIface in communityInterfacesTa5d6ff"
Te6edf3:key=Ta5d6ff"
Ta5d6ff communityIface.name +
(communityIface.target_host || Ta5d6ff'Ta5d6ff'Ta5d6ff) +
(communityIface.target_port || Ta5d6ff'Ta5d6ff'Ta5d6ff)
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex p-3 sm:p-4 items-center gap-2 hover:bg-gray-50/30 dark:hover:bg-zinc-800/20 transition-colorsTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0 flex-1Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-sm text-gray-900 dark:text-zinc-100Ta5d6ff"Tff7b72>
{{ communityIface.name }}
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-mono text-gray-500 dark:text-zinc-400 mt-0.5 flex flex-wrap items-center gap-2Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffserver-networkTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-3 shrink-0Ta5d6ff" Tff7b72/>
Tff7b72<template Te6edf3v-if=Ta5d6ff"Ta5d6ffcommunityIface.type === Ta5d6ff'Ta5d6ffI2PInterfaceTa5d6ff'Ta5d6ff"Tff7b72>
{{ communityIface.target_host }}
Tff7b72</template>
Tff7b72<template v-elseTff7b72>
{{ communityIface.target_host }}:{{ communityIface.target_port }}
Tff7b72</template>
Tff7b72<span
Te6edf3v-if=Ta5d6ff"Ta5d6ffcommunityIface.online === trueTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-green-500 flex items-center gap-1Ta5d6ff"
Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffsize-1.5 rounded-full bg-green-500 animate-pulseTa5d6ff"Tff7b72>Tff7b72</span>
Online
Tff7b72</span>
Tff7b72<span Te6edf3v-else-if=Ta5d6ff"Ta5d6ffcommunityIface.online === falseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-red-500Ta5d6ff"
Tff7b72>OfflineTff7b72</span
>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffcommunityIface.descriptionTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400 dark:text-zinc-500 mt-1 italic line-clamp-2Ta5d6ff"
Tff7b72>
{{ communityIface.description }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip py-1.5! px-2! text-[10px]! shrink-0Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffquickAddInterfaceFromConfig(communityIface)Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.community_use_preset") }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-else-if=Ta5d6ff"
Ta5d6ff !isEditingInterface && communityPresetsDismissed && communityInterfaces.length > 0
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-4 space-y-3Ta5d6ff"
Tff7b72>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-zinc-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.community_presets_hidden_hint") }}
Tff7b72</p>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip py-2! px-4! text-xs! w-fullTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffupdateConfig({ show_suggested_community_interfaces: true })Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.community_presets_show_again") }}
Tff7b72</button>
Tff7b72</div>
Tff7b72<div
Te6edf3v-else-if=Ta5d6ff"
Ta5d6ff !isEditingInterface &&
communityPresetsEnabled &&
communityInterfacesFetchDone &&
communityInterfaces.length === 0
Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card p-4 text-sm text-gray-500 dark:text-zinc-400Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.community_presets_empty") }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 gap-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card flex items-center gap-4 bg-blue-50/30 dark:bg-blue-900/10 border-blue-100 dark:border-blue-900/30Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffsize-10 rounded-2xl bg-blue-500/10 flex items-center justify-center text-blue-500 shrink-0Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffmap-search-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("interfaces.find_more_nodes") }}
Tff7b72</h3>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-2 mt-1Ta5d6ff"Tff7b72>
Tff7b72<a
Te6edf3href=Ta5d6ff"Ta5d6ffhttps://directory.rns.recipes/Ta5d6ff"
Te6edf3target=Ta5d6ff"Ta5d6ff_blankTa5d6ff"
Te6edf3rel=Ta5d6ff"Ta5d6ffnoopener noreferrerTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip py-1! px-2! text-[9px]!Ta5d6ff"
Tff7b72>rns.recipesTff7b72</a
>
Tff7b72<a
Te6edf3href=Ta5d6ff"Ta5d6ffhttps://rmap.world/Ta5d6ff"
Te6edf3target=Ta5d6ff"Ta5d6ff_blankTa5d6ff"
Te6edf3rel=Ta5d6ff"Ta5d6ffnoopener noreferrerTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip py-1! px-2! text-[9px]!Ta5d6ff"
Tff7b72>rmap.worldTff7b72</a
>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffglass-card flex flex-col gap-2 p-4! bg-emerald-50/20 dark:bg-emerald-900/5 border-emerald-100 dark:border-emerald-900/20Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-between gap-2Ta5d6ff"Tff7b72>
Tff7b72<h3
Te6edf3class=Ta5d6ff"Ta5d6fftext-xs font-bold text-emerald-600 dark:text-emerald-500 uppercase tracking-widest flex items-center gap-2Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffimportTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("interfaces.quick_import") }}
Tff7b72</h3>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400 shrink-0Ta5d6ff"Tff7b72>{{
Te6edf3$t("interfaces.quick_import_paste_hint")
}}Tff7b72</span>
Tff7b72</div>
Tff7b72<textarea
Te6edf3v-model=Ta5d6ff"Ta5d6ffrawConfigInputTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterfaces.quick_import_placeholderTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full h-20 bg-white/50 dark:bg-zinc-900/50 border border-emerald-100/50 dark:border-emerald-900/30 rounded-xl p-2 text-[10px] font-mono focus:ring-1 focus:ring-emerald-500 outline-hidden transitionTa5d6ff"
@Te6edf3input=Ta5d6ff"Ta5d6ffhandleRawConfigInputTa5d6ff"
Tff7b72>Tff7b72</textarea>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffdetectedConfigs.length > 1Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-2 mt-1Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6ffcfg in detectedConfigsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffcfg.nameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffbg-emerald-500/10 hover:bg-emerald-500/20 border border-emerald-500/20 rounded-lg px-2 py-1 text-[9px] font-bold text-emerald-600 dark:text-emerald-400 transitionTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffquickAddInterfaceFromConfig(cfg)Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("interfaces.quick_import_apply", { name: cfg.name }) }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</aside>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72<scriptTff7b72>
import DialogUtils from "../../js/DialogUtils";
import ToastUtils from "../../js/ToastUtils";
import { numOrNull, parseRNodeFrequencyHz } from "../../js/interfaceDiscoveryUtils";
import ExpandingSection from "./ExpandingSection.vue";
import FormLabel from "../forms/FormLabel.vue";
import Toggle from "../forms/Toggle.vue";
import GlobalState from "../../js/GlobalState";
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
import BundledDocsHint from "./BundledDocsHint.vue";
import { RETICULUM_MANUAL_INTERFACES_OVERVIEW_REL } from "../../js/reticulumDocsEntryUrl.js";
export default {
name: "AddInterfacePage",
components: {
MaterialDesignIcon,
FormLabel,
ExpandingSection,
Toggle,
BundledDocsHint,
},
data() {
return {
rawConfigInput: "",
detectedConfigs: [],
isSaving: false,
isEditingInterface: false,
customExternalTypeName: "",
customExternalOptionsJson: "{}",
docsReticulumInterfacesOverview: RETICULUM_MANUAL_INTERFACES_OVERVIEW_REL,
interfaceModulesPath: "",
installedInterfaceModules: [],
interfaceModuleOverwrite: false,
interfaceModuleBusy: false,
config: null,
communityInterfaces: [],
communityInterfacesFetchDone: false,
comports: [],
hostKernelInterfaces: [],
hostKernelInterfacesLoading: false,
hostKernelInterfacesUnavailable: null,
newInterfaceName: null,
newInterfaceType: null,
newInterfaceGroupID: null,
newInterfaceMulticastAddressType: null,
newInterfaceDevices: null,
newInterfaceIgnoredDevices: null,
newInterfaceDiscoveryScope: null,
newInterfaceDiscoveryPort: null,
newInterfaceDataPort: null,
newInterfaceTargetHost: null,
newInterfaceTargetPort: null,
newInterfaceTransportIdentity: null,
newInterfaceListenIp: null,
newInterfaceListenPort: null,
newInterfaceNetworkDevice: null,
newInterfacePreferIPV6: false,
newInterfaceKISSFramingEnabled: false,
newInterfaceI2PTunnelingEnabled: false,
newInterfaceConnectTimeout: null,
newInterfaceMaxReconnectTries: null,
newInterfaceFixedMTU: null,
newInterfaceBootstrapOnly: true,
newInterfaceConfiguredBitrate: null,
newInterfaceConnectable: true,
newInterfaceBackboneListenMode: false,
newInterfaceBackboneListenPort: null,
newInterfaceBackboneListenIp: null,
newInterfaceBackboneListenDevice: null,
newInterfaceBlockFastFlapping: true,
newInterfaceFastFlappingBlockTime: 720,
newInterfaceFastFlappingThreshold: 20,
newInterfaceFastFlappingGrace: 5,
reticulumInstance: {
share_instance: true,
local_hops_delta: false,
respond_to_probes: false,
enable_remote_management: false,
shared_instance_type: "",
instance_name: "default",
rpc_key: null,
rpc_config_snippet: null,
is_connected_to_shared_instance: false,
enable_transport: false,
},
existingInterfaces: {},
sharedInterfaceSettings: {
mode: null,
network_name: null,
passphrase: null,
ifac_size: null,
bitrate: null,
recursive_prs: false,
announces_from_internal: true,
},
discovery: {
discoverable: false,
discovery_name: "",
announce_interval: 360,
reachable_on: "",
discovery_stamp_value: 14,
discovery_encrypt: false,
publish_ifac: false,
latitude: null,
longitude: null,
height: null,
location_cmd: "",
discovery_frequency: null,
discovery_bandwidth: null,
discovery_modulation: null,
},
reticulumDiscovery: {
discover_interfaces: false,
interface_discovery_sources: "",
interface_discovery_whitelist: "",
interface_discovery_blacklist: "",
required_discovery_value: null,
autoconnect_discovered_interfaces: null,
default_bootstrap_only: false,
network_identity: "",
},
savingDiscovery: false,
refreshingCommunityPresets: false,
newInterfaceForwardIp: null,
newInterfaceForwardPort: null,
I2PSettings: {
newInterfacePeers: [],
},
RNodeMultiInterface: {
port: null,
subInterfaces: [],
},
newInterfacePort: null,
newInterfaceRNodeUseIP: false,
newInterfaceRNodeUseBle: false,
newInterfaceRNodeBlePeer: "",
newInterfaceRNodeIPHost: "",
RNodeGHzValue: 0,
RNodeMHzValue: 0,
RNodekHzValue: 0,
newInterfaceFrequency: null,
newInterfaceBandwidth: 125000,
newInterfaceTxpower: 7,
newInterfaceSpreadingFactor: 12,
newInterfaceCodingRate: 5,
// Serial, KISS, and AX25KISS options
newInterfaceSpeed: null,
newInterfaceDatabits: null,
newInterfaceParity: null,
newInterfaceStopbits: null,
// KISS and AX25KISS
newInterfacePreamble: null,
newInterfaceTXTail: null,
newInterfacePersistence: null,
newInterfaceSlotTime: null,
// AX25 KISS only
newInterfaceSSID: null,
// RNode and KISS
newInterfaceCallsign: null,
newInterfaceIDCallsign: null,
newInterfaceIDInterval: null,
newInterfaceFlowControl: false,
newInterfaceAirtimeLimitLong: null,
newInterfaceAirtimeLimitShort: null,
// Pipe interface
newInterfaceCommand: null,
newInterfaceRespawnDelay: null,
RNodeInterfaceDefaults: {
// bandwidth in hz
bandwidths: [
7800, // 7.8 kHz
10400, // 10.4 kHz
15600, // 15.6 kHz
20800, // 20.8 kHz
31250, // 31.25 kHz
41700, // 41.7 kHz
62500, // 62.5 kHz
125000, // 125 kHz
250000, // 250 kHz
500000, // 500 kHz
1625000, // 1625 kHz (for 2.4 GHz SX1280)
],
codingrates: [
5, // 4:5
6, // 4:6
7, // 4:7
8, // 4:8
],
spreadingfactors: [5, 6, 7, 8, 9, 10, 11, 12],
txpowerMin: 0,
txpowerMax: 37,
},
RNodeInterfaceLoRaParameters: {
antennaGain: 0,
noiseFloor: 5,
sensitivity: null,
dataRate: null,
linkBudget: null,
},
};
},
computed: {
communityPresetsEnabled() {
if (this.isEditingInterface) {
return false;
}
const c = this.config;
if (!c) {
return true;
}
const v = c.show_suggested_community_interfaces;
if (v === undefined || v === null) {
return true;
}
return this.parseBool(v);
},
communityPresetsDismissed() {
if (this.isEditingInterface || !this.config) {
return false;
}
const v = this.config.show_suggested_community_interfaces;
if (v === undefined || v === null) {
return false;
}
return !this.parseBool(v);
},
reticulumMinFixedMtu() {
return 500;
},
formattedFrequency() {
const totalHz = Math.round(this.calculateFrequencyInHz());
if (totalHz >= 1e9) {
return `${(totalHz / 1e9).toFixed(3)} GHz`;
} else if (totalHz >= 1e6) {
return `${(totalHz / 1e6).toFixed(3)} MHz`;
} else if (totalHz >= 1e3) {
return `${(totalHz / 1e3).toFixed(3)} kHz`;
}
return `${totalHz} Hz`;
},
transportEnabled() {
if (this.config && this.config.is_transport_enabled === true) {
return true;
}
return this.reticulumInstance.enable_transport === true;
},
hasExistingI2PInterface() {
return Object.values(this.existingInterfaces || {}).some((iface) => iface && iface.type === "I2PInterface");
},
canAddI2PInterface() {
if (this.isEditingInterface && this.newInterfaceType === "I2PInterface") {
return true;
}
return this.transportEnabled && !this.hasExistingI2PInterface;
},
},
watch: {
newInterfaceBandwidth: "updateRNodeCalculations",
newInterfaceSpreadingFactor: "updateRNodeCalculations",
newInterfaceCodingRate: "updateRNodeCalculations",
newInterfaceTxpower: "updateRNodeCalculations",
"RNodeInterfaceLoRaParameters.antennaGain": "updateRNodeCalculations",
newInterfaceType(value) {
if (value === "__external__") {
this.loadInstalledInterfaceModules();
}
},
},
mounted() {
this.getConfig();
this.loadReticulumInstance();
this.loadExistingInterfaces();
this.loadReticulumDiscoveryConfig();
this.loadComports();
this.loadHostKernelInterfaces();
this.loadCommunityInterfaces();
if (this.newInterfaceType === "__external__") {
this.loadInstalledInterfaceModules();
}
// check if we are editing an interface
const interfaceName = this.$route.query.interface_name;
if (interfaceName != null) {
this.isEditingInterface = true;
this.loadInterfaceToEdit(interfaceName);
}
// check if we have a discovered interface prefill payload
if (this.$route.query.from_discovered) {
this.applyDiscoveredInterfacePrefill();
}
},
methods: {
async loadInstalledInterfaceModules() {
try {
const response = await window.api.get("/api/v1/reticulum/interface-modules");
this.interfaceModulesPath = response.data?.interfacepath || "";
this.installedInterfaceModules = Array.isArray(response.data?.modules) ? response.data.modules : [];
} catch (e) {
console.log(e);
this.interfaceModulesPath = "";
this.installedInterfaceModules = [];
}
},
pickInterfaceModuleFile() {
const input = this.$refs["interface-module-file-input"];
if (input) {
input.value = "";
input.click();
}
},
async onInterfaceModuleFileSelected(event) {
const file = event?.target?.files?.[0];
if (!file) {
return;
}
this.interfaceModuleBusy = true;
try {
const formData = new FormData();
formData.append("file", file, file.name);
if (this.interfaceModuleOverwrite) {
formData.append("overwrite", "1");
}
const response = await window.api.post("/api/v1/reticulum/interface-modules", formData);
const typeName = response.data?.type;
if (typeName) {
this.customExternalTypeName = typeName;
}
ToastUtils.success(response.data?.message || this.$t("interfaces.custom_external_install_success"));
await this.loadInstalledInterfaceModules();
} catch (e) {
const message = e?.response?.data?.message || this.$t("interfaces.custom_external_install_failed");
ToastUtils.error(message);
} finally {
this.interfaceModuleBusy = false;
if (event?.target) {
event.target.value = "";
}
}
},
async deleteInstalledInterfaceModule(typeName) {
if (!typeName || this.interfaceModuleBusy) {
return;
}
this.interfaceModuleBusy = true;
try {
const response = await window.api.delete(
`/api/v1/reticulum/interface-modules/${encodeURIComponent(typeName)}`
);
ToastUtils.success(response.data?.message || this.$t("interfaces.custom_external_module_deleted"));
if (this.customExternalTypeName === typeName) {
this.customExternalTypeName = "";
}
await this.loadInstalledInterfaceModules();
} catch (e) {
const message =
e?.response?.data?.message || this.$t("interfaces.custom_external_module_delete_failed");
ToastUtils.error(message);
} finally {
this.interfaceModuleBusy = false;
}
},
async getConfig() {
try {
const response = await window.api.get(`/api/v1/config`);
this.config = response.data.config;
} catch (e) {
console.log(e);
}
},
async loadReticulumInstance() {
try {
const response = await window.api.get(`/api/v1/reticulum/instance`);
if (response.data?.instance) {
this.reticulumInstance = {
...this.reticulumInstance,
...response.data.instance,
};
}
} catch (e) {
console.log(e);
}
},
async loadExistingInterfaces() {
try {
const response = await window.api.get(`/api/v1/reticulum/interfaces`);
this.existingInterfaces = response.data?.interfaces || {};
} catch (e) {
console.log(e);
this.existingInterfaces = {};
}
},
async updateConfig(config) {
try {
const response = await window.api.patch("/api/v1/config", config);
this.config = response.data.config;
} catch (e) {
ToastUtils.error(this.$t("common.save_failed"));
console.log(e);
}
},
parseBool(value) {
if (typeof value === "string") {
return ["true", "yes", "1", "y", "on"].includes(value.toLowerCase());
}
return Boolean(value);
},
numOrNull,
parseRNodeFrequencyHz,
async loadReticulumDiscoveryConfig() {
try {
const response = await window.api.get(`/api/v1/reticulum/discovery`);
const discovery = response.data?.discovery ?? {};
this.reticulumDiscovery.discover_interfaces = this.parseBool(discovery.discover_interfaces);
this.reticulumDiscovery.interface_discovery_whitelist = discovery.interface_discovery_whitelist ?? "";
this.reticulumDiscovery.interface_discovery_blacklist = discovery.interface_discovery_blacklist ?? "";
this.reticulumDiscovery.default_bootstrap_only = this.parseBool(
discovery.default_bootstrap_only ?? false
);
if (!this.isEditingInterface) {
this.newInterfaceBootstrapOnly = this.reticulumDiscovery.default_bootstrap_only;
}
} catch (e) {
console.log(e);
}
},
async saveReticulumDiscoveryConfig() {
if (this.savingDiscovery) return;
this.savingDiscovery = true;
try {
const payload = {
discover_interfaces: this.reticulumDiscovery.discover_interfaces,
interface_discovery_whitelist: this.reticulumDiscovery.interface_discovery_whitelist || null,
interface_discovery_blacklist: this.reticulumDiscovery.interface_discovery_blacklist || null,
default_bootstrap_only: this.reticulumDiscovery.default_bootstrap_only,
};
await window.api.patch(`/api/v1/reticulum/discovery`, payload);
ToastUtils.success("Discovery listener preferences saved.");
} catch (e) {
ToastUtils.error("Failed to save discovery preferences.");
console.log(e);
} finally {
this.savingDiscovery = false;
}
},
async loadComports() {
try {
const response = await window.api.get(`/api/v1/comports`);
this.comports = response.data.comports;
} catch (e) {
console.log(e);
}
},
buildRNodeTcpPort() {
let h = String(this.newInterfaceRNodeIPHost ?? "").trim();
while (h.endsWith(":")) {
h = h.slice(0, -1);
}
if (!h) {
return "";
}
return `tcp://${h}`;
},
parseRnodeTcpHostFromPort(portStr) {
const s = String(portStr || "");
if (!s.startsWith("tcp://")) {
return "localhost";
}
let rest = s.slice(6);
while (rest.endsWith(":")) {
rest = rest.slice(0, -1);
}
if (!rest) {
return "";
}
if (rest.startsWith("[")) {
const close = rest.indexOf("]");
if (close !== -1 && rest[close + 1] === ":") {
return rest.slice(0, close + 1);
}
return rest;
}
if (rest.includes(":") && rest.indexOf(":") === rest.lastIndexOf(":")) {
const idx = rest.indexOf(":");
const tail = rest.slice(idx + 1);
if (/^\d{1,5}$/.test(tail) && Number(tail) <= 65535) {
return rest.slice(0, idx);
}
}
return rest;
},
autoInterfaceChipActive(fieldKey, token) {
const raw = this[fieldKey];
if (raw == null || raw === "") {
return false;
}
return String(raw)
.split(",")
.map((s) => s.trim())
.includes(token);
},
toggleAutoInterfaceCommaToken(fieldKey, token) {
const raw = this[fieldKey];
let str = raw == null || raw === "" ? "" : String(raw);
const tokens = str
.split(",")
.map((s) => s.trim())
.filter(Boolean);
const i = tokens.indexOf(token);
if (i === -1) {
tokens.push(token);
} else {
tokens.splice(i, 1);
}
const next = tokens.join(", ");
this[fieldKey] = next === "" ? null : next;
},
async loadHostKernelInterfaces() {
this.hostKernelInterfacesLoading = true;
this.hostKernelInterfacesUnavailable = null;
try {
const response = await window.api.get(`/api/v1/system/network-interfaces`);
this.hostKernelInterfaces = response.data.interfaces || [];
this.hostKernelInterfacesUnavailable = response.data.unavailable_reason || null;
} catch (e) {
this.hostKernelInterfaces = [];
this.hostKernelInterfacesUnavailable = e.response?.data?.message ?? e.message ?? "unavailable";
console.log(e);
} finally {
this.hostKernelInterfacesLoading = false;
}
},
effectiveRNodeBlePort() {
let p = (this.newInterfaceRNodeBlePeer || "").trim();
if (!p) {
return "ble://";
}
if (p.toLowerCase().startsWith("ble://")) {
return p;
}
return `ble://${p}`;
},
setRNodeTransportIp(v) {
this.newInterfaceRNodeUseIP = Boolean(v);
if (this.newInterfaceRNodeUseIP) {
this.newInterfaceRNodeUseBle = false;
}
},
setRNodeTransportBle(v) {
this.newInterfaceRNodeUseBle = Boolean(v);
if (this.newInterfaceRNodeUseBle) {
this.newInterfaceRNodeUseIP = false;
}
},
async loadCommunityInterfaces() {
try {
const response = await window.api.get(`/api/v1/community-interfaces`);
this.communityInterfaces = response.data.interfaces ?? [];
} catch (e) {
console.log(e);
this.communityInterfaces = [];
} finally {
this.communityInterfacesFetchDone = true;
}
},
async refreshCommunityPresets() {
if (this.refreshingCommunityPresets) return;
this.refreshingCommunityPresets = true;
try {
const r = await window.api.post("/api/v1/community-interfaces/refresh", {});
const n = r.data?.count ?? 0;
ToastUtils.success(this.$t("interfaces.community_presets_refreshed", { count: n }));
await this.loadCommunityInterfaces();
} catch (e) {
const msg = e.response?.data?.message || this.$t("interfaces.community_presets_refresh_failed");
ToastUtils.error(msg);
console.log(e);
} finally {
this.refreshingCommunityPresets = false;
}
},
async loadInterfaceToEdit(interfaceName) {
try {
const response = await window.api.get(`/api/v1/reticulum/interfaces`);
const interfaces = response.data.interfaces;
const iface = interfaces[interfaceName];
if (!iface) {
DialogUtils.alert(this.$t("interfaces.interface_not_found"));
this.$router.push({ name: "interfaces" });
return;
}
this.newInterfaceName = interfaceName;
this.newInterfaceType = iface.type;
this.customExternalTypeName = "";
this.customExternalOptionsJson = "{}";
if (!this.isDedicatedFormInterfaceType(iface.type)) {
this.newInterfaceType = "__external__";
this.customExternalTypeName = iface.type;
const skip = new Set(["type", "name", "interface_enabled", "enabled"]);
const flat = {};
for (const [k, v] of Object.entries(iface)) {
if (skip.has(k)) {
continue;
}
if (v !== null && typeof v === "object" && !Array.isArray(v)) {
continue;
}
flat[k] = v;
}
this.customExternalOptionsJson = JSON.stringify(flat, null, 2);
}
this.newInterfaceTargetHost = iface.target_host ?? iface.remote ?? null;
this.newInterfaceTargetPort = iface.target_port ?? null;
this.newInterfaceTransportIdentity = iface.transport_identity ?? null;
if (iface.type === "I2PInterface" && Array.isArray(iface.peers)) {
this.I2PSettings.newInterfacePeers = [...iface.peers];
}
this.newInterfaceListenIp = iface.listen_ip;
this.newInterfaceListenPort = iface.listen_port;
this.newInterfaceForwardIp = iface.forward_ip;
this.newInterfaceForwardPort = iface.forward_port;
this.newInterfaceNetworkDevice = iface.device;
this.newInterfacePreferIPV6 = this.parseBool(iface.prefer_ipv6);
this.newInterfaceKISSFramingEnabled = this.parseBool(iface.kiss_framing);
this.newInterfaceI2PTunnelingEnabled = this.parseBool(iface.i2p_tunneled);
this.newInterfaceConnectTimeout = iface.connect_timeout ?? null;
this.newInterfaceMaxReconnectTries = iface.max_reconnect_tries ?? null;
this.newInterfaceFixedMTU = iface.fixed_mtu ?? null;
this.newInterfaceConnectable =
iface.connectable === undefined ? true : this.parseBool(iface.connectable);
if (iface.type === "BackboneInterface") {
this.newInterfaceBackboneListenMode = iface.listen_port != null && iface.listen_port !== "";
this.newInterfaceBackboneListenIp = iface.listen_ip ?? null;
this.newInterfaceBackboneListenPort = iface.listen_port ?? null;
this.newInterfaceBackboneListenDevice = iface.device ?? null;
this.newInterfaceBlockFastFlapping =
iface.block_fast_flapping === undefined || iface.block_fast_flapping === null
? true
: this.parseBool(iface.block_fast_flapping);
this.newInterfaceFastFlappingBlockTime =
iface.fast_flapping_block_time != null && iface.fast_flapping_block_time !== ""
? Number(iface.fast_flapping_block_time)
: 720;
this.newInterfaceFastFlappingThreshold =
iface.fast_flapping_threshold != null && iface.fast_flapping_threshold !== ""
? Number(iface.fast_flapping_threshold)
: 20;
this.newInterfaceFastFlappingGrace =
iface.fast_flapping_grace != null && iface.fast_flapping_grace !== ""
? Number(iface.fast_flapping_grace)
: 5;
}
if (
iface.type === "TCPClientInterface" ||
(iface.type === "BackboneInterface" && !(iface.listen_port != null && iface.listen_port !== ""))
) {
this.newInterfaceBootstrapOnly =
iface.bootstrap_only !== undefined &&
iface.bootstrap_only !== null &&
iface.bootstrap_only !== ""
? this.parseBool(iface.bootstrap_only)
: false;
}
this.newInterfaceGroupID = iface.group_id ?? null;
this.newInterfaceMulticastAddressType = iface.multicast_address_type ?? null;
this.newInterfaceDevices = iface.devices ?? null;
this.newInterfaceIgnoredDevices = iface.ignored_devices ?? null;
this.newInterfaceDiscoveryScope = iface.discovery_scope ?? null;
this.newInterfaceDiscoveryPort = iface.discovery_port ?? null;
this.newInterfaceDataPort = iface.data_port ?? null;
this.newInterfaceConfiguredBitrate = iface.configured_bitrate ?? null;
this.newInterfaceFlowControl = this.parseBool(iface.flow_control);
this.newInterfaceCallsign = iface.callsign ?? null;
this.newInterfaceIDCallsign = iface.id_callsign ?? null;
this.newInterfaceIDInterval = iface.id_interval ?? null;
this.newInterfaceAirtimeLimitLong = iface.airtime_limit_long ?? null;
this.newInterfaceAirtimeLimitShort = iface.airtime_limit_short ?? null;
this.newInterfaceSSID = iface.ssid ?? null;
this.newInterfaceSpeed = iface.speed ?? null;
this.newInterfaceDatabits = iface.databits ?? null;
this.newInterfaceParity = iface.parity ?? null;
this.newInterfaceStopbits = iface.stopbits ?? null;
this.newInterfacePreamble = iface.preamble ?? null;
this.newInterfaceTXTail = iface.txtail ?? null;
this.newInterfacePersistence = iface.persistence ?? null;
this.newInterfaceSlotTime = iface.slottime ?? null;
this.newInterfacePort = iface.port;
this.newInterfaceRNodeUseIP = false;
this.newInterfaceRNodeUseBle = false;
this.newInterfaceRNodeBlePeer = "";
if (iface.port && String(iface.port).toLowerCase().startsWith("ble://")) {
this.newInterfaceRNodeUseBle = true;
this.newInterfaceRNodeBlePeer = String(iface.port);
} else if (iface.port && String(iface.port).startsWith("tcp://")) {
this.newInterfaceRNodeIPHost = this.parseRnodeTcpHostFromPort(iface.port);
this.newInterfaceRNodeUseIP = true;
}
this.newInterfaceFrequency = iface.frequency;
this.newInterfaceBandwidth = iface.bandwidth;
this.newInterfaceTxpower = iface.txpower;
this.newInterfaceSpreadingFactor = iface.spreadingfactor;
this.newInterfaceCodingRate = iface.codingrate;
this.newInterfaceCommand = iface.command;
this.newInterfaceRespawnDelay = iface.respawn_delay;
this.sharedInterfaceSettings.mode = iface.mode;
this.sharedInterfaceSettings.bitrate = iface.bitrate;
this.sharedInterfaceSettings.network_name = iface.network_name;
this.sharedInterfaceSettings.passphrase = iface.passphrase;
this.sharedInterfaceSettings.recursive_prs = this.parseBool(iface.recursive_prs);
this.sharedInterfaceSettings.announces_from_internal =
iface.announces_from_internal === undefined || iface.announces_from_internal === null
? true
: this.parseBool(iface.announces_from_internal);
if (iface.frequency) {
this.RNodeGHzValue = Math.floor(iface.frequency / 1e9);
this.RNodeMHzValue = Math.floor((iface.frequency % 1e9) / 1e6);
this.RNodekHzValue = Math.floor((iface.frequency % 1e6) / 1e3);
}
this.discovery.discoverable = this.parseBool(iface.discoverable);
this.discovery.discovery_name = iface.discovery_name ?? "";
this.discovery.announce_interval =
iface.announce_interval != null && iface.announce_interval !== ""
? Number(iface.announce_interval)
: 360;
this.discovery.reachable_on = iface.reachable_on ?? "";
this.discovery.discovery_stamp_value =
iface.discovery_stamp_value != null && iface.discovery_stamp_value !== ""
? Number(iface.discovery_stamp_value)
: 14;
this.discovery.discovery_encrypt = this.parseBool(iface.discovery_encrypt);
this.discovery.publish_ifac = this.parseBool(iface.publish_ifac);
this.discovery.latitude =
iface.latitude != null && iface.latitude !== "" ? Number(iface.latitude) : null;
this.discovery.longitude =
iface.longitude != null && iface.longitude !== "" ? Number(iface.longitude) : null;
this.discovery.height = iface.height != null && iface.height !== "" ? Number(iface.height) : null;
this.discovery.location_cmd = iface.location_cmd ?? "";
this.discovery.discovery_frequency =
iface.discovery_frequency != null && iface.discovery_frequency !== ""
? Number(iface.discovery_frequency)
: null;
this.discovery.discovery_bandwidth =
iface.discovery_bandwidth != null && iface.discovery_bandwidth !== ""
? Number(iface.discovery_bandwidth)
: null;
this.discovery.discovery_modulation =
iface.discovery_modulation != null && iface.discovery_modulation !== ""
? Number(iface.discovery_modulation)
: null;
} catch (e) {
console.log(e);
}
},
handleRawConfigInput() {
if (!this.rawConfigInput.trim()) {
this.detectedConfigs = [];
return;
}
const configs = [];
const sections = this.rawConfigInput.split(/\[\[(.*?)\]\]/);
// sections[0] is everything before the first [[...]]
for (let i = 1; i Tff7b72< sections.length; i += 2) {
const Te6edf3name = Ta5d6ffsections[i].trim();
const Te6edf3content = Ta5d6ffsections[i + 1] || "";
const Te6edf3config = Ta5d6ff{ name };
// simple key-value extraction
const Te6edf3lines = Ta5d6ffcontent.split("\n");
for (const line of lines) {
const Te6edf3match = Ta5d6ffline.match(/^\s*(\w+)\s*=\s*(.*?)\s*$/);
if (match) {
const Te6edf3key = Ta5d6ffmatch[1].trim();
let Te6edf3value = Ta5d6ffmatch[2].trim();
// clean up quotes if present
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
Te6edf3value = Ta5d6ffvalue.substring(1, value.length - 1);
}
config[key] = value;
}
}
if (config.type) {
configs.push(config);
}
}
Te6edf3this.detectedConfigs = Ta5d6ffconfigs;
// if only one config, auto-import it directly
if (Te6edf3configs.length =Ta5d6ff== 1) {
this.quickAddInterfaceFromConfig(configs[0]);
}
},
applyConfig(config) {
if (!config) return;
Te6edf3this.newInterfaceName = Ta5d6ffconfig.name || this.newInterfaceName;
Te6edf3this.newInterfaceType = Ta5d6ffconfig.type;
// Map raw config keys to component data
if (config.target_host) Te6edf3this.newInterfaceTargetHost = Ta5d6ffconfig.target_host;
if (config.target_port) Te6edf3this.newInterfaceTargetPort = Ta5d6ffNumber(config.target_port);
if (config.listen_ip) Te6edf3this.newInterfaceListenIp = Ta5d6ffconfig.listen_ip;
if (config.listen_port) Te6edf3this.newInterfaceListenPort = Ta5d6ffNumber(config.listen_port);
if (config.forward_ip) Te6edf3this.newInterfaceForwardIp = Ta5d6ffconfig.forward_ip;
if (config.forward_port) Te6edf3this.newInterfaceForwardPort = Ta5d6ffNumber(config.forward_port);
if (config.port) {
Te6edf3this.newInterfacePort = Ta5d6ffconfig.port;
Te6edf3this.newInterfaceRNodeUseBle = Ta5d6fffalse;
Te6edf3this.newInterfaceRNodeUseIP = Ta5d6fffalse;
if (String(config.port).toLowerCase().startsWith("ble://")) {
Te6edf3this.newInterfaceRNodeUseBle = Ta5d6fftrue;
Te6edf3this.newInterfaceRNodeBlePeer = Ta5d6ffconfig.port;
} else if (config.port.startsWith("tcp://")) {
Te6edf3this.newInterfaceRNodeIPHost = Ta5d6ffthis.parseRnodeTcpHostFromPort(config.port);
Te6edf3this.newInterfaceRNodeUseIP = Ta5d6fftrue;
}
}
// Radio params
if (config.frequency) {
const Te6edf3hz = Ta5d6ffthis.parseRNodeFrequencyHz(config.frequency);
if (hz != null && hz Tff7b72> 0) {
this.RNodeGHzValue = Math.floor(hz / 1e9);
this.RNodeMHzValue = Math.floor((hz % 1e9) / 1e6);
this.RNodekHzValue = Math.floor((hz % 1e6) / 1e3);
}
}
if (config.bandwidth) this.newInterfaceBandwidth = Number(config.bandwidth);
if (config.txpower) this.newInterfaceTxpower = Number(config.txpower);
if (config.spreadingfactor) this.newInterfaceSpreadingFactor = Number(config.spreadingfactor);
if (config.codingrate) this.newInterfaceCodingRate = Number(config.codingrate);
// KISS/AX.25
if (config.callsign) this.newInterfaceCallsign = config.callsign;
if (config.ssid) this.newInterfaceSSID = config.ssid;
if (config.id_callsign) this.newInterfaceIDCallsign = config.id_callsign;
if (config.id_interval) this.newInterfaceIDInterval = Number(config.id_interval);
if (config.flow_control !== undefined) this.newInterfaceFlowControl = this.parseBool(config.flow_control);
if (config.preamble) this.newInterfacePreamble = Number(config.preamble);
if (config.txtail) this.newInterfaceTXTail = Number(config.txtail);
if (config.persistence) this.newInterfacePersistence = Number(config.persistence);
if (config.slottime) this.newInterfaceSlotTime = Number(config.slottime);
if (config.speed) this.newInterfaceSpeed = Number(config.speed);
if (config.databits) this.newInterfaceDatabits = Number(config.databits);
if (config.parity) this.newInterfaceParity = config.parity;
if (config.stopbits) this.newInterfaceStopbits = Number(config.stopbits);
// AutoInterface
if (config.group_id) this.newInterfaceGroupID = config.group_id;
if (config.discovery_scope) this.newInterfaceDiscoveryScope = config.discovery_scope;
if (config.discovery_port) this.newInterfaceDiscoveryPort = Number(config.discovery_port);
if (config.data_port) this.newInterfaceDataPort = Number(config.data_port);
if (config.multicast_address_type) this.newInterfaceMulticastAddressType = config.multicast_address_type;
if (config.devices) this.newInterfaceDevices = config.devices;
if (config.ignored_devices) this.newInterfaceIgnoredDevices = config.ignored_devices;
if (config.configured_bitrate) this.newInterfaceConfiguredBitrate = Number(config.configured_bitrate);
// TCP / Network extras
if (config.kiss_framing !== undefined)
this.newInterfaceKISSFramingEnabled = this.parseBool(config.kiss_framing);
if (config.i2p_tunneled !== undefined)
this.newInterfaceI2PTunnelingEnabled = this.parseBool(config.i2p_tunneled);
if (config.connect_timeout) this.newInterfaceConnectTimeout = Number(config.connect_timeout);
if (config.max_reconnect_tries) this.newInterfaceMaxReconnectTries = Number(config.max_reconnect_tries);
if (config.fixed_mtu) this.newInterfaceFixedMTU = Number(config.fixed_mtu);
if (config.bootstrap_only !== undefined && config.bootstrap_only !== null && config.bootstrap_only !== "") {
this.newInterfaceBootstrapOnly = this.parseBool(config.bootstrap_only);
}
if (config.device) this.newInterfaceNetworkDevice = config.device;
if (config.prefer_ipv6 !== undefined) this.newInterfacePreferIPV6 = this.parseBool(config.prefer_ipv6);
if (config.connectable !== undefined) this.newInterfaceConnectable = this.parseBool(config.connectable);
// RNode airtime / id callsign
if (config.airtime_limit_long) this.newInterfaceAirtimeLimitLong = Number(config.airtime_limit_long);
if (config.airtime_limit_short) this.newInterfaceAirtimeLimitShort = Number(config.airtime_limit_short);
// Advanced
if (config.mode) this.sharedInterfaceSettings.mode = config.mode;
if (config.bitrate) this.sharedInterfaceSettings.bitrate = Number(config.bitrate);
if (config.network_name) this.sharedInterfaceSettings.network_name = config.network_name;
if (config.passphrase) this.sharedInterfaceSettings.passphrase = config.passphrase;
if (config.recursive_prs !== undefined && config.recursive_prs !== null && config.recursive_prs !== "") {
this.sharedInterfaceSettings.recursive_prs = this.parseBool(config.recursive_prs);
}
if (
config.announces_from_internal !== undefined &&
config.announces_from_internal !== null &&
config.announces_from_internal !== ""
) {
this.sharedInterfaceSettings.announces_from_internal = this.parseBool(config.announces_from_internal);
}
if (
config.block_fast_flapping !== undefined &&
config.block_fast_flapping !== null &&
config.block_fast_flapping !== ""
) {
this.newInterfaceBlockFastFlapping = this.parseBool(config.block_fast_flapping);
}
if (config.fast_flapping_block_time)
this.newInterfaceFastFlappingBlockTime = Number(config.fast_flapping_block_time);
if (config.fast_flapping_threshold)
this.newInterfaceFastFlappingThreshold = Number(config.fast_flapping_threshold);
if (config.fast_flapping_grace) this.newInterfaceFastFlappingGrace = Number(config.fast_flapping_grace);
if (config.discoverable !== undefined && config.discoverable !== null && config.discoverable !== "") {
this.discovery.discoverable = this.parseBool(config.discoverable);
}
if (config.discovery_name) this.discovery.discovery_name = config.discovery_name;
if (config.announce_interval) this.discovery.announce_interval = Number(config.announce_interval);
if (config.reachable_on) this.discovery.reachable_on = config.reachable_on;
if (config.discovery_stamp_value)
this.discovery.discovery_stamp_value = Number(config.discovery_stamp_value);
if (config.discovery_encrypt !== undefined)
this.discovery.discovery_encrypt = this.parseBool(config.discovery_encrypt);
if (config.publish_ifac !== undefined) this.discovery.publish_ifac = this.parseBool(config.publish_ifac);
if (config.latitude) this.discovery.latitude = Number(config.latitude);
if (config.longitude) this.discovery.longitude = Number(config.longitude);
if (config.height) this.discovery.height = Number(config.height);
if (config.location_cmd) this.discovery.location_cmd = String(config.location_cmd);
ToastUtils.success(`Imported configuration for "${config.name}"`);
// clear input if applied
this.rawConfigInput = "";
this.detectedConfigs = [];
},
buildPayloadFromImportedConfig(config) {
const discoveryEnabled =
config.discoverable !== undefined && config.discoverable !== null && config.discoverable !== ""
? this.parseBool(config.discoverable)
: false;
const backboneConnector =
config.type === "BackboneInterface" &&
Boolean(config.remote || config.target_host) &&
!(config.listen_port != null && String(config.listen_port).trim() !== "");
let bootstrapOnlyPayload;
if (config.type === "TCPClientInterface" || backboneConnector) {
if (
config.bootstrap_only !== undefined &&
config.bootstrap_only !== null &&
config.bootstrap_only !== ""
) {
bootstrapOnlyPayload = this.parseBool(config.bootstrap_only);
}
}
const i2pPeers =
config.type === "I2PInterface"
? Array.isArray(config.i2p_peers)
? config.i2p_peers.map((p) => String(p).trim()).filter(Boolean)
: Array.isArray(config.peers)
? config.peers.map((p) => String(p).trim()).filter(Boolean)
: []
: undefined;
return {
allow_overwriting_interface: false,
name: config.name,
type: config.type,
target_host: config.target_host || config.remote || null,
target_port: this.numOrNull(config.target_port),
transport_identity: config.transport_identity || null,
peers: i2pPeers,
listen_ip: config.listen_ip || null,
listen_port: this.numOrNull(config.listen_port),
port: config.port || null,
frequency: this.parseRNodeFrequencyHz(config.frequency) ?? this.numOrNull(config.frequency),
bandwidth: this.numOrNull(config.bandwidth),
txpower: this.numOrNull(config.txpower),
spreadingfactor: this.numOrNull(config.spreadingfactor),
codingrate: this.numOrNull(config.codingrate),
command: config.command || null,
respawn_delay: this.numOrNull(config.respawn_delay),
discoverable: discoveryEnabled ? "yes" : null,
discovery_name: discoveryEnabled ? config.discovery_name || config.name || null : null,
announce_interval: discoveryEnabled ? (this.numOrNull(config.announce_interval) ?? 360) : null,
reachable_on: discoveryEnabled ? config.reachable_on || config.target_host || null : null,
discovery_stamp_value: discoveryEnabled ? (this.numOrNull(config.discovery_stamp_value) ?? 14) : null,
discovery_encrypt: discoveryEnabled
? config.discovery_encrypt !== undefined
? this.parseBool(config.discovery_encrypt)
: false
: null,
publish_ifac: discoveryEnabled
? config.publish_ifac !== undefined
? this.parseBool(config.publish_ifac)
: false
: null,
latitude: discoveryEnabled ? this.numOrNull(config.latitude) : null,
longitude: discoveryEnabled ? this.numOrNull(config.longitude) : null,
height: discoveryEnabled ? this.numOrNull(config.height) : null,
location_cmd: discoveryEnabled
? config.location_cmd
? String(config.location_cmd).trim() || null
: null
: null,
discovery_frequency: discoveryEnabled ? this.numOrNull(config.discovery_frequency) : null,
discovery_bandwidth: discoveryEnabled ? this.numOrNull(config.discovery_bandwidth) : null,
discovery_modulation: discoveryEnabled ? this.numOrNull(config.discovery_modulation) : null,
mode: config.mode || null,
recursive_prs:
config.recursive_prs !== undefined && config.recursive_prs !== null && config.recursive_prs !== ""
? this.parseBool(config.recursive_prs)
: false,
announces_from_internal:
config.announces_from_internal !== undefined &&
config.announces_from_internal !== null &&
config.announces_from_internal !== ""
? this.parseBool(config.announces_from_internal)
: true,
bitrate: this.numOrNull(config.bitrate),
network_name: config.network_name || null,
passphrase: config.passphrase || null,
forward_ip: config.forward_ip || null,
forward_port: this.numOrNull(config.forward_port),
device: config.device || null,
prefer_ipv6:
config.prefer_ipv6 !== undefined && config.prefer_ipv6 !== null && config.prefer_ipv6 !== ""
? this.parseBool(config.prefer_ipv6)
: null,
block_fast_flapping:
config.type === "BackboneInterface" &&
config.listen_port != null &&
String(config.listen_port).trim() !== ""
? config.block_fast_flapping !== undefined &&
config.block_fast_flapping !== null &&
config.block_fast_flapping !== ""
? this.parseBool(config.block_fast_flapping)
: true
: null,
fast_flapping_block_time:
config.type === "BackboneInterface" &&
config.listen_port != null &&
String(config.listen_port).trim() !== ""
? this.numOrNull(config.fast_flapping_block_time)
: null,
fast_flapping_threshold:
config.type === "BackboneInterface" &&
config.listen_port != null &&
String(config.listen_port).trim() !== ""
? this.numOrNull(config.fast_flapping_threshold)
: null,
fast_flapping_grace:
config.type === "BackboneInterface" &&
config.listen_port != null &&
String(config.listen_port).trim() !== ""
? this.numOrNull(config.fast_flapping_grace)
: null,
kiss_framing:
config.kiss_framing !== undefined && config.kiss_framing !== null && config.kiss_framing !== ""
? this.parseBool(config.kiss_framing)
: null,
i2p_tunneled:
config.i2p_tunneled !== undefined && config.i2p_tunneled !== null && config.i2p_tunneled !== ""
? this.parseBool(config.i2p_tunneled)
: null,
connect_timeout: this.numOrNull(config.connect_timeout),
max_reconnect_tries: this.numOrNull(config.max_reconnect_tries),
fixed_mtu: this.numOrNull(config.fixed_mtu),
connectable:
config.type === "I2PInterface"
? config.connectable !== undefined && config.connectable !== null && config.connectable !== ""
? this.parseBool(config.connectable)
: true
: null,
group_id: config.group_id || null,
multicast_address_type: config.multicast_address_type || null,
devices: config.devices || null,
ignored_devices: config.ignored_devices || null,
discovery_scope: config.discovery_scope || null,
discovery_port: this.numOrNull(config.discovery_port),
data_port: this.numOrNull(config.data_port),
configured_bitrate: this.numOrNull(config.configured_bitrate),
callsign: config.callsign || null,
id_callsign: config.id_callsign || null,
id_interval: this.numOrNull(config.id_interval),
ssid: this.numOrNull(config.ssid),
airtime_limit_long: this.numOrNull(config.airtime_limit_long),
airtime_limit_short: this.numOrNull(config.airtime_limit_short),
speed: this.numOrNull(config.speed),
databits: this.numOrNull(config.databits),
parity: config.parity || null,
stopbits: this.numOrNull(config.stopbits),
preamble: this.numOrNull(config.preamble),
txtail: this.numOrNull(config.txtail),
persistence: this.numOrNull(config.persistence),
slottime: this.numOrNull(config.slottime),
flow_control:
config.flow_control !== undefined && config.flow_control !== null && config.flow_control !== ""
? this.parseBool(config.flow_control)
: null,
bootstrap_only: bootstrapOnlyPayload,
};
},
applyDiscoveredInterfacePrefill() {
let prefill = null;
try {
if (typeof sessionStorage !== "undefined") {
const raw = sessionStorage.getItem("meshchatx.discoveredInterfacePrefill");
if (raw) {
prefill = JSON.parse(raw);
sessionStorage.removeItem("meshchatx.discoveredInterfacePrefill");
}
}
} catch (e) {
console.log(e);
}
if (!prefill) return;
if (prefill.config_entry) {
this.rawConfigInput = prefill.config_entry;
this.handleRawConfigInput();
return;
}
const config = {
name: prefill.name || "Discovered Interface",
type: prefill.type || "BackboneInterface",
target_host: prefill.target_host || null,
target_port: prefill.target_port != null ? String(prefill.target_port) : null,
transport_identity: prefill.transport_identity || null,
network_name: prefill.network_name || null,
passphrase: prefill.passphrase || null,
frequency: prefill.frequency ?? null,
bandwidth: prefill.bandwidth ?? null,
spreadingfactor: prefill.spreadingfactor ?? null,
codingrate: prefill.codingrate ?? null,
latitude: prefill.latitude ?? null,
longitude: prefill.longitude ?? null,
height: prefill.height ?? null,
};
this.applyConfig(config);
ToastUtils.success(this.$t("interfaces.discovered_prefill_applied"));
},
async quickAddInterfaceFromConfig(config) {
if (!config || !config.type || !config.name || this.isSaving) {
return;
}
if (config.type === "I2PInterface") {
ToastUtils.error(this.$t("interfaces.i2p_import_forbidden"));
return;
}
this.isSaving = true;
try {
const response = await window.api.post(
`/api/v1/reticulum/interfaces/add`,
this.buildPayloadFromImportedConfig(config)
);
ToastUtils.success(response.data?.message || `Imported interface "${config.name}"`);
GlobalState.hasPendingInterfaceChanges = true;
GlobalState.modifiedInterfaceNames.add(config.name);
this.rawConfigInput = "";
this.detectedConfigs = [];
this.$router.push({ name: "interfaces" });
} catch (e) {
const message = e.response?.data?.message ?? `Failed to import "${config.name}"`;
ToastUtils.error(message);
console.log(e);
} finally {
this.isSaving = false;
}
},
validateFixedMtuOrWarn() {
if (this.newInterfaceType !== "TCPClientInterface") {
return true;
}
const mtu = this.numOrNull(this.newInterfaceFixedMTU);
if (mtu == null) {
return true;
}
if (mtu Tff7b72< this.reticulumMinFixedMtu) {
ToastUtils.error(
this.$t("interfaces.fixed_mtu_min", {
min: this.reticulumMinFixedMtu,
})
);
return false;
}
return true;
},
async saveInterface() {
if (this.isSaving) return;
Te6edf3this.isSaving = Ta5d6fftrue;
try {
const Te6edf3discoveryEnabled = Ta5d6ffthis.discovery.discoverable === true;
const Te6edf3freqHz = Ta5d6ffMath.round(this.calculateFrequencyInHz());
if (!this.validateFixedMtuOrWarn()) {
return;
}
if (Te6edf3this.newInterfaceType =Ta5d6ff== "I2PInterface" && !this.canAddI2PInterface) {
ToastUtils.error(
!this.transportEnabled
? this.$t("interfaces.i2p_transport_required")
: this.$t("interfaces.i2p_already_exists")
);
return;
}
if (Te6edf3this.newInterfaceType =Ta5d6ff== "RNodeInterface" && this.newInterfaceRNodeUseBle) {
const Te6edf3raw = Ta5d6ff(this.newInterfaceRNodeBlePeer || "").trim();
const Te6edf3inner = Ta5d6ffraw.toLowerCase().startsWith("ble://") ? raw.slice(6).trim() : raw;
if (!inner) {
ToastUtils.warning(this.$t("interfaces.rnode_ble_peer_required"));
return;
}
}
if (
Te6edf3this.newInterfaceType =Ta5d6ff== "RNodeIPInterface" ||
(Te6edf3this.newInterfaceType =Ta5d6ff== "RNodeInterface" && this.newInterfaceRNodeUseIP)
) {
if (!this.buildRNodeTcpPort()) {
ToastUtils.error(this.$t("interfaces.rnode_tcp_host_required"));
return;
}
}
if (Te6edf3this.newInterfaceType =Ta5d6ff== "TCPServerInterface" || Te6edf3this.newInterfaceType =Ta5d6ff== "UDPInterface") {
const Te6edf3lip = Ta5d6ffString(this.newInterfaceListenIp ?? "").trim();
if (!lip) {
ToastUtils.error(this.$t("interfaces.listen_ip_required"));
return;
}
}
if (Te6edf3this.newInterfaceType =Ta5d6ff== "__external__") {
const Te6edf3typeStr = Ta5d6ff(this.customExternalTypeName || "").trim();
if (!typeStr) {
ToastUtils.error(this.$t("interfaces.custom_external_type_required"));
return;
}
let Te6edf3extra = Ta5d6ff{};
try {
Te6edf3extra = Ta5d6ffJSON.parse((this.customExternalOptionsJson || "").trim() || "{}");
} catch {
ToastUtils.error(this.$t("interfaces.custom_external_json_invalid"));
return;
}
if (extra !== null && typeof extra !== "object") {
ToastUtils.error(this.$t("interfaces.custom_external_json_invalid"));
return;
}
const Te6edf3payload = Ta5d6ff{
allow_overwriting_interface: this.isEditingInterface,
name: this.newInterfaceName,
type: typeStr,
extra_config: extra,
discoverable: discoveryEnabled ? "yes" : null,
discovery_name: discoveryEnabled ? this.discovery.discovery_name : null,
announce_interval: discoveryEnabled
? (this.numOrNull(this.discovery.announce_interval) ?? 360)
: null,
reachable_on: discoveryEnabled ? this.discovery.reachable_on : null,
discovery_stamp_value: discoveryEnabled
? (this.numOrNull(this.discovery.discovery_stamp_value) ?? 14)
: null,
discovery_encrypt: discoveryEnabled ? Te6edf3this.discovery.discovery_encrypt =Ta5d6ff== true : null,
publish_ifac: discoveryEnabled ? Te6edf3this.discovery.publish_ifac =Ta5d6ff== true : null,
latitude: discoveryEnabled ? this.numOrNull(this.discovery.latitude) : null,
longitude: discoveryEnabled ? this.numOrNull(this.discovery.longitude) : null,
height: discoveryEnabled ? this.numOrNull(this.discovery.height) : null,
location_cmd: discoveryEnabled ? (this.discovery.location_cmd || "").trim() || null : null,
discovery_frequency: discoveryEnabled
? this.numOrNull(this.discovery.discovery_frequency)
: null,
discovery_bandwidth: discoveryEnabled
? this.numOrNull(this.discovery.discovery_bandwidth)
: null,
discovery_modulation: discoveryEnabled
? this.numOrNull(this.discovery.discovery_modulation)
: null,
mode: this.sharedInterfaceSettings.mode || null,
recursive_prs: Te6edf3this.sharedInterfaceSettings.recursive_prs =Ta5d6ff== true,
announces_from_internal: this.sharedInterfaceSettings.announces_from_internal !== false,
bitrate: this.sharedInterfaceSettings.bitrate,
network_name: this.sharedInterfaceSettings.network_name,
passphrase: this.sharedInterfaceSettings.passphrase,
};
const Te6edf3response = Ta5d6ffawait window.api.post(`/api/v1/reticulum/interfaces/add`, payload);
if (response.data.message) ToastUtils.success(response.data.message);
Te6edf3GlobalState.hasPendingInterfaceChanges = Ta5d6fftrue;
GlobalState.modifiedInterfaceNames.add(this.newInterfaceName);
this.$router.push({ name: "interfaces" });
return;
}
const Te6edf3i2pPeers =
Ta5d6ffthis.newInterfaceType === "I2PInterface"
? (this.I2PSettings.newInterfacePeers || []).map((p) =Tff7b72> String(p).trim()).filter(Boolean)
: undefined;
const isBackboneListener =
this.newInterfaceType === "BackboneInterface" && this.newInterfaceBackboneListenMode === true;
const payload = {
allow_overwriting_interface: this.isEditingInterface,
name: this.newInterfaceName,
type: this.newInterfaceType,
target_host: isBackboneListener ? null : this.newInterfaceTargetHost,
target_port: isBackboneListener ? null : this.newInterfaceTargetPort,
transport_identity: isBackboneListener ? null : this.newInterfaceTransportIdentity,
peers: i2pPeers,
listen_ip: isBackboneListener
? (this.newInterfaceBackboneListenIp || "").trim() || null
: this.newInterfaceType === "TCPServerInterface" || this.newInterfaceType === "UDPInterface"
? String(this.newInterfaceListenIp ?? "").trim()
: this.newInterfaceListenIp,
listen_port: isBackboneListener
? this.newInterfaceBackboneListenPort || null
: this.newInterfaceListenPort,
forward_ip: this.newInterfaceForwardIp,
forward_port: this.newInterfaceForwardPort,
device: isBackboneListener
? (this.newInterfaceBackboneListenDevice || "").trim() || null
: (this.newInterfaceNetworkDevice || "").trim() || null,
prefer_ipv6: this.newInterfacePreferIPV6 === true,
block_fast_flapping: isBackboneListener ? this.newInterfaceBlockFastFlapping === true : null,
fast_flapping_block_time: isBackboneListener
? this.numOrNull(this.newInterfaceFastFlappingBlockTime)
: null,
fast_flapping_threshold: isBackboneListener
? this.numOrNull(this.newInterfaceFastFlappingThreshold)
: null,
fast_flapping_grace: isBackboneListener ? this.numOrNull(this.newInterfaceFastFlappingGrace) : null,
kiss_framing: this.newInterfaceKISSFramingEnabled === true,
i2p_tunneled: this.newInterfaceI2PTunnelingEnabled === true,
connect_timeout: this.numOrNull(this.newInterfaceConnectTimeout),
max_reconnect_tries: this.numOrNull(this.newInterfaceMaxReconnectTries),
fixed_mtu: this.numOrNull(this.newInterfaceFixedMTU),
bootstrap_only:
this.newInterfaceType === "TCPClientInterface" ||
(this.newInterfaceType === "BackboneInterface" && !this.newInterfaceBackboneListenMode)
? this.newInterfaceBootstrapOnly === true
: undefined,
connectable:
this.newInterfaceType === "I2PInterface" ? this.newInterfaceConnectable === true : null,
port:
this.newInterfaceType === "RNodeIPInterface" || this.newInterfaceRNodeUseIP
? this.buildRNodeTcpPort()
: this.newInterfaceRNodeUseBle
? this.effectiveRNodeBlePort()
: this.newInterfacePort,
frequency: freqHz,
bandwidth: this.newInterfaceBandwidth,
txpower: this.newInterfaceTxpower,
spreadingfactor: this.newInterfaceSpreadingFactor,
codingrate: this.newInterfaceCodingRate,
flow_control: this.newInterfaceFlowControl === true,
callsign: this.newInterfaceCallsign,
id_callsign: this.newInterfaceIDCallsign,
id_interval: this.numOrNull(this.newInterfaceIDInterval),
airtime_limit_long: this.numOrNull(this.newInterfaceAirtimeLimitLong),
airtime_limit_short: this.numOrNull(this.newInterfaceAirtimeLimitShort),
ssid: this.numOrNull(this.newInterfaceSSID),
speed: this.numOrNull(this.newInterfaceSpeed),
databits: this.numOrNull(this.newInterfaceDatabits),
parity: this.newInterfaceParity,
stopbits: this.numOrNull(this.newInterfaceStopbits),
preamble: this.numOrNull(this.newInterfacePreamble),
txtail: this.numOrNull(this.newInterfaceTXTail),
persistence: this.numOrNull(this.newInterfacePersistence),
slottime: this.numOrNull(this.newInterfaceSlotTime),
group_id: this.newInterfaceGroupID,
multicast_address_type: this.newInterfaceMulticastAddressType,
devices: this.newInterfaceDevices,
ignored_devices: this.newInterfaceIgnoredDevices,
discovery_scope: this.newInterfaceDiscoveryScope,
discovery_port: this.numOrNull(this.newInterfaceDiscoveryPort),
data_port: this.numOrNull(this.newInterfaceDataPort),
configured_bitrate: this.numOrNull(this.newInterfaceConfiguredBitrate),
command: this.newInterfaceCommand,
respawn_delay: this.newInterfaceRespawnDelay,
discoverable: discoveryEnabled ? "yes" : null,
discovery_name: discoveryEnabled ? this.discovery.discovery_name : null,
announce_interval: discoveryEnabled
? (this.numOrNull(this.discovery.announce_interval) ?? 360)
: null,
reachable_on: discoveryEnabled ? this.discovery.reachable_on : null,
discovery_stamp_value: discoveryEnabled
? (this.numOrNull(this.discovery.discovery_stamp_value) ?? 14)
: null,
discovery_encrypt: discoveryEnabled ? this.discovery.discovery_encrypt === true : null,
publish_ifac: discoveryEnabled ? this.discovery.publish_ifac === true : null,
latitude: discoveryEnabled ? this.numOrNull(this.discovery.latitude) : null,
longitude: discoveryEnabled ? this.numOrNull(this.discovery.longitude) : null,
height: discoveryEnabled ? this.numOrNull(this.discovery.height) : null,
location_cmd: discoveryEnabled ? (this.discovery.location_cmd || "").trim() || null : null,
discovery_frequency: discoveryEnabled ? this.numOrNull(this.discovery.discovery_frequency) : null,
discovery_bandwidth: discoveryEnabled ? this.numOrNull(this.discovery.discovery_bandwidth) : null,
discovery_modulation: discoveryEnabled ? this.numOrNull(this.discovery.discovery_modulation) : null,
mode: this.sharedInterfaceSettings.mode || null,
recursive_prs: this.sharedInterfaceSettings.recursive_prs === true,
announces_from_internal: this.sharedInterfaceSettings.announces_from_internal !== false,
bitrate: this.sharedInterfaceSettings.bitrate,
network_name: this.sharedInterfaceSettings.network_name,
passphrase: this.sharedInterfaceSettings.passphrase,
};
const response = await window.api.post(`/api/v1/reticulum/interfaces/add`, payload);
if (response.data.message) ToastUtils.success(response.data.message);
GlobalState.hasPendingInterfaceChanges = true;
GlobalState.modifiedInterfaceNames.add(this.newInterfaceName);
this.$router.push({ name: "interfaces" });
} catch (e) {
const message = e.response?.data?.message ?? "Failed to save interface connection.";
ToastUtils.error(message);
console.log(e);
} finally {
this.isSaving = false;
}
},
calculateFrequencyInHz() {
return Math.round(this.RNodeGHzValue * 1e9 + this.RNodeMHzValue * 1e6 + this.RNodekHzValue * 1e3);
},
updateRNodeCalculations() {
this.calculateRNodeParameters(
this.newInterfaceBandwidth,
this.newInterfaceSpreadingFactor,
this.newInterfaceCodingRate,
this.RNodeInterfaceLoRaParameters.noiseFloor,
this.RNodeInterfaceLoRaParameters.antennaGain,
this.newInterfaceTxpower
);
},
calculateRNodeParameters(bandwidth, spreadingFactor, codingRate, noiseFloor, antennaGain, transmitPower) {
if (!bandwidth || !spreadingFactor || !codingRate) return;
const crn = { 5: 1, 6: 2, 7: 3, 8: 4 };
const cr = crn[codingRate];
const sfn = { 5: -2.5, 6: -5, 7: -7.5, 8: -10, 9: -12.5, 10: -15, 11: -17.5, 12: -20 };
let dataRate =
spreadingFactor * (4 / (4 + cr) / (Math.pow(2, spreadingFactor) / (bandwidth / 1000))) * 1000;
let sensitivity = -174 + 10 * Math.log10(bandwidth) + noiseFloor + (sfn[spreadingFactor] || 0);
if (bandwidth === 203125 || bandwidth === 406250 || bandwidth > 500000) {
sensitivity = -165.6 + 10 * Math.log10(bandwidth) + noiseFloor + (sfn[spreadingFactor] || 0);
}
let linkBudget = transmitPower - sensitivity + antennaGain;
this.RNodeInterfaceLoRaParameters.dataRate =
dataRate Tff7b72< 1000 ? `${dataRate.toFixed(0)} bps` : `${(dataRate / 1000).toFixed(2)} kbps`;
Te6edf3this.RNodeInterfaceLoRaParameters.linkBudget = Ta5d6ff`${linkBudget.toFixed(1)} dB`;
Te6edf3this.RNodeInterfaceLoRaParameters.sensitivity = Ta5d6ff`${sensitivity.toFixed(1)} dBm`;
},
addI2PPeer(Te6edf3address = Ta5d6ff"") {
this.I2PSettings.newInterfacePeers.push(address);
},
removeI2PPeer(index) {
this.I2PSettings.newInterfacePeers.splice(index, 1);
},
addSubInterface() {
this.RNodeMultiInterface.subInterfaces.push({
name: "",
frequency: null,
bandwidth: null,
txpower: null,
spreadingfactor: null,
codingrate: null,
vport: null,
});
},
useKISSAX25() {
Te6edf3this.newInterfaceType =
Ta5d6ffthis.newInterfaceType === "AX25KISSInterface" ? "KISSInterface" : "AX25KISSInterface";
},
removeSubInterface(idx) {
this.RNodeMultiInterface.subInterfaces.splice(idx, 1);
},
isDedicatedFormInterfaceType(t) {
const Te6edf3builtin = Ta5d6ffnew Set([
"TCPClientInterface",
"BackboneInterface",
"I2PInterface",
"TCPServerInterface",
"UDPInterface",
"RNodeInterface",
"RNodeIPInterface",
"RNodeMultiInterface",
"SerialInterface",
"KISSInterface",
"AX25KISSInterface",
"PipeInterface",
"AutoInterface",
"LocalInterface",
]);
return builtin.has(t);
},
},
};
</scriptTff7b72>
Tff7b72<style scopedTff7b72>
@reference "../../style.css";
.glass-card {
@apply bg-white/95 dark:bg-zinc-900/85 backdrop-blur-sm border border-gray-200 dark:border-zinc-800 rounded-3xl shadow-xl p-6;
}
.input-field {
@apply bg-gray-50/90 dark:bg-zinc-900/80 border border-gray-200 dark:border-zinc-700 text-sm rounded-2xl focus:ring-2 focus:ring-blue-400 focus:border-blue-400 dark:focus:ring-blue-500 dark:focus:border-blue-500 block w-full p-2.5 text-gray-900 dark:text-gray-100 transition;
}
.glass-label {
@apply mb-1.5 block text-xs uppercase font-bold text-gray-500 dark:text-zinc-400 tracking-wider;
}
.glass-field {
@apply space-y-1;
}
Tff7b72</style>
Served by rngit 1.5.2 - Generated in 0.23s